Я максимально постараюсь писать без «воды». Минимум лишней отвлекающей информации и разглагольствований. Максимум полезной информации и рабочего кода. Я не буду поднимать вопрос зачем кому-то собственный торрент-поисковик на базе RuTracker. И я не считаю себя гуру программирования. Мы просто сделаем этот сайт вместе. Будем использовать Apache+PHP, MySQL и Sphinx. Сразу предупрежу, что на минимальном виртуальном хостинге сайт будет работать совсем не быстро.

image

База данных


Для начала нам надо взять саму базу. RuTracker каждый месяц выкладывает дамп своих торрентов здесь. Скачиваем, распаковываем и видим два десятка файлов CSV.

image

Нам нужны только те, в которых есть информация о торрентах – остальные удаляем. В файле «category_info.csv» — подсказка для тех, кому не хочется открывать каждый файл (удалить: «category_1.csv», «category_4.csv», «category_36.csv»). Открываем любой из оставшихся файлов и видим такую структуру (я сразу заменил символ «;» на новую строку, что бы было визуально удобнее):
«1568» ID раздела на RuTracker
«Кулинария» Название раздела
«63629» ID темы на RuTracker
«F7D7BE97A818CCDFA072C42348EB669F7883888D» Hash торрента
"(Кулинария) Вкусные истории 1" Название торрента
«729927066» Размер раздачи в байтах
«2006-08-21 10:00:22» Дата публикации раздачи

Теперь мы добавим всю информацию в базу данных. Используем MySQL, как самую распространённую БД. У меня получилась вот такая таблица (обратите внимание: столбец «hash» — уникальный, все текстовые данные в кодировке utf8):

Таблица SQL
CREATE TABLE IF NOT EXISTS `torrents` (
  `id` int(11) NOT NULL,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `hash` varchar(40) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
  `date` date NOT NULL,
  `size` int(11) NOT NULL,
  `topic_id` int(11) NOT NULL,
  `cat_id` int(11) NOT NULL,
  `cat_name` varchar(120) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf32 COLLATE=utf32_bin;

ALTER TABLE `torrents`
  ADD PRIMARY KEY (`id`),
  ADD UNIQUE KEY `hash` (`hash`);

ALTER TABLE `torrents`
  MODIFY `id` int(11) NOT NULL AUTO_INCREMENT;


Затем закачиваем все файлы CSV в одну папку (например, назовем её «db») на сервере. Добавление информации о торрентах в БД осуществим с помощью не сложного скрипта, представленного ниже. Его тоже необходимо закачать в ту же папку где находятся исходные CSV-файлы.

Файл insert_to_db.php
<?
//Ограничиваем время выполнения скрипта 3-мя минутами
set_time_limit(180);


//Подключаемся к MySQL, при неудаче выводим ошибку
mysql_connect("localhost", "torrent", "password") or die("Could not connect to MySQL");


//Выбираем БД, при неудаче выводим ошибку
mysql_select_db("torrent") or die("Could not select database");


//Переводим все общение с БД в кодировку utf8
mysql_query("SET NAMES utf8");


//Открываем файл указанный в url переменной "f"
$fp = fopen($_GET[f], "r");


//Запускаем цикл до конца строк в файле
while (!feof($fp)) {
        //Считываем строку (да, функцию trim() выполнять не обязательно, но у каждого программиста свои "тараканы")
        $tmp = trim(fgets($fp));
        
        
        //Преобразуем строку в массив. За разделитель используем ";"
        $torrent = explode('";"', $tmp);
        
        
        //В первом и последнем элементе удаляем лишние символы "
        $torrent[0] = substr($torrent[0], 1);
        $torrent[6] = substr($torrent[6], 0, (strlen($torrent[6]) - 1));
        
        
        //Если раскомментировать следующую строку, то можно увидеть как распарсился первый торрент в файле
        //print '<pre>'; print_r($torrent); exit();
        
        
        //Вставляем данные текущего торрента в таблицу
        mysql_query("INSERT INTO `torrents` 
            (`name`,
            `hash`,
            `date`,
            `size`,
            `topic_id`,
            `cat_id`,
            `cat_name`) 
          VALUES 
            ('" . mysql_real_escape_string($torrent[4]) . "',
            '" . $torrent[3] . "',
            '" . $torrent[6] . "',
            '" . $torrent[5] . "',
            '" . $torrent[2] . "',
            '" . $torrent[0] . "',
            '" . mysql_real_escape_string($torrent[1]) . "')
        ");
}
//Закрываем файл
fclose($fp);

//Выводим сообщение о завершении работы
print 'complete: ' . $_GET[f];
?>



Открываем браузер, открываем url «http://site.ru/db/insert_to_db.php?f=category_10.csv». Проделываем тоже самое с каждым файлом CSV. Да, все это можно было автоматизировать, но я специально написал так, что бы было максимально всё понятно. После этих действий в нашей таблице оказалось чуть больше 1,6 миллиона записей. Не маленькая такая база. Поиск MySQL с таким объемом данных не справится, так что поручим эту задачу Sphinx.

image

Sphinx


Установка Sphinx на различные системы производится разными способами. Все зависит от операционной системы и железа. Это тема заслуживает отдельной статьи. Но есть очень много отличных мануалов в Интернете. На русском языке тоже. Сейчас же мы займемся настройкой конфигурационного файла для Sphinx. Создаем в корневом каталоге сайта директорию, допустим, cache. Здесь будут хранится все файлы индекса Sphinx для нашего сайта. Загружаем в эту папку файл конфигурации (листинг приведен ниже).

Файл torrents.conf
# Настройка источника откуда берутся данные
source torrentz
{
        # Подключаемся к БД
        type = mysql
        sql_host = localhost
        sql_user = torrent
        sql_pass = password
        sql_db = torrent
        sql_port = 3306

        # Переводим все общение с БД в кодировку utf8
        sql_query_pre = SET NAMES utf8
        sql_query_pre = SET CHARACTER SET utf8

        # Запрос данных для индексации
        sql_query = SELECT id, name FROM torrents

        # Время (в миллисекундах) паузы перед посылкой запроса БД. Используется для медленных и загруженных серверов
        sql_ranged_throttle = 0
}


# Настройка индекса. Более подробно все описано в документации Sphinx
index torrentz
{
        # Выбор источника
        source = torrentz

        # Путь до файлов индекса
        path = /home/rutr/rutracker.online/www/cache/

        # Способ хранения индекса 
        docinfo = extern

        # Использование английской и русской морфологии
        morphology = stem_enru

        # Минимальная длина индексируемого слова
        min_word_len = 2

        # Установка кодировки
        charset_type = utf-8

        # Символы
        charset_table = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F

        # Минимальная длина инфикса 
        min_infix_len = 2

        # Использовать оператор усечения "*"
        enable_star = 1
}


# Настройка индексатора
indexer
{
        # Лимит используемой оперативной памяти
        mem_limit = 32M
}


# Настройка поискового демона
searchd
{
		# Указываем порт на который сайт будет отдавать запросы на поиск
		listen = 127.0.0.1:3312

        # Лог
        log = /home/rutr/rutracker.online/www/cache/searchd.log

        # Лог запросов
        query_log = /home/rutr/rutracker.online/www/cache/query.log

        # Таймаут на соединение с сервером
        read_timeout = 5

        # Максимальное кол-во потомков от процесса
        max_children = 30

        # Путь до pid-файла
        pid_file = /home/rutr/rutracker.online/www/cache/searchd.pid

        # Максимальное кол-во результатов выдачи
        max_matches = 1000
}


Подключаемся к серверу через ssh. Для того что бы Sphinx смог искать по нашей базе, надо подготовить индекс. Выполняем команду:

indexer --config /home/rutr/rutracker.online/www/cache/torrents.conf –all

Sphinx некоторое время будет проводить индекс базы данных. Длительность зависит от мощности сервера. В моем случае индексирование заняло около 10 минут.

image

После окончания индексирования, проверим все ли нормально прошло. Для этого выполним поиск через консоль с помощью команды (поисковая фраза пишется после указания файла конфига):

search --config /home/rutr/rutracker.online/www/cache/torrents.conf morrowind mod

image

Если вы увидели что-то похожее на верхний скриншот, значит индексирование прошло успешно. Если ничего не найдено, то не нужно запускать следующую команду. Для запуска демона поиска Sphinx выполняем команду:

searchd --config /home/rutr/rutracker.online/www/cache/torrents.conf 

image

Обратите внимание, что демон нужно запускать после каждой перезагрузки системы. Для выключения демона (если нужно) добавляем «--stop» в конце вышеуказанной команды.

Web


Я не долго думал какой фреймворк использовать для web-интерфейса. Требования простые: простота в использовании, адаптивный дизайн и поддержка всех современных браузеров. Под это отлично подходит, пусть и немного надоевший, Bootstrap. Дистрибутив качать не обязательно, можно подключить файл стилей онлайн. Главная страница на чистом HTML, без использования PHP. Комментарии к коду, думаю, будут излишни.

Файл index.php
<!DOCTYPE html>
<html lang="ru">
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="icon" href="/favicon.ico">
		<title>Зеркало раздач RuTracker</title>
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
		<style type="text/css">
			.inCenter {
			    margin: auto;
			    position: absolute;
			    top: 0;
			    left: 0;
			    bottom: 0;
			    right: 0;
			}
			.inCenter.isResp {
			    width: 50%;
			    height: 50%;
			    min-width: 400px;
			    max-width: 800px;
			    padding: 40px;
			}
		</style>
	</head>
	<body>
		<div class="container">
			<div class="row">
				<div class="inCenter isResp">
					<div class="col-sm-12 col-md-10 col-md-offset-1">
						<form action="search.php" method="GET">
							<div class="form-group text-center">
								<h1>Зеркало раздач RuTracker</h1>
							</div>
							<div class="form-group input-group">
								<input class="form-control input-lg" type="text" name="q" placeholder=""/>      
								<span class="input-group-btn">
									<button class="btn btn-primary input-lg" type="submit"><i class="glyphicon glyphicon-search"></i></button>
								</span>	                
							</div>
						</form>
					</div>
				</div>
			</div>
		</div>
	</body>
</html>


Дизайн главной страницы получился очень минималистичным и максимально функциональным.

image

Скрипт поиска будет уже интереснее. Для начала нам нужен API Sphinx на PHP. Последнюю версию можно взять здесь. Кратко расскажу, как работает скрипт поиска, а подробнее уже в листинге. Подключаем файл для работы с API, настраиваем поиск, ищем, выкладываем результаты поиска в удобном виде. Скачать торрент можно будет прямо из поиска, без дополнительных кликов.

Файл search.php
<?
//Фильтруем и форматируем запрос
$q=trim(urldecode($_GET[q]));


//Если нет запроса на поиск, то делаем редирект на главную страницу
if (empty($q)) {header("Location: /"); exit();}


//Если запрос есть, то...
//Подключаемся к MySQL, при неудаче выводим ошибку
mysql_connect("localhost", "torrent", "password") or die("Could not connect to MySQL");


//Выбираем БД, при неудаче выводим ошибку
mysql_select_db("torrent") or die("Could not select database");


//Переводим все общение с БД в кодировку utf8
mysql_query("SET NAMES utf8");

//Подключаем API Sphinx
include("sphinxapi.php");


//Создаем объект Sphinx 
$sphinx=new SphinxClient();


//Подключаемся к Sphinx-серверу. Порт мы указываем в файле "torrents.conf"
$sphinx->SetServer('localhost', 3312);


//Ищем совадение по любомым словом
$sphinx->SetMatchMode(SPH_MATCH_ANY);


//Сортируем результаты по релевантности
$sphinx->SetSortMode(SPH_SORT_RELEVANCE);


//Выводим 50 результатов начиная с первого.
$sphinx->SetLimits(0, 50);

    
//Запускаем поиск (* - использование всех индексов в файле "torrents.conf", но он у нас один: torrentz)
$torrents=$sphinx->Query($q, '*');


//Если раскомментировать следующие две строки, то можно увидеть ошибки и как отработал поиск
//print $sphinx->getLastError();
//print '<br><pre>'; print_r($torrents); exit();



//Функция перевода байтов в килобайты, мегабайты и тд. Пригодится нам ниже. Описывать её прицип не имеем смысла - чистая арифметика. 
function bytesToSize($bytes, $precision = 0)
{   
        $kilobyte = 1024;
        $megabyte = $kilobyte * 1024;
        $gigabyte = $megabyte * 1024;
        $terabyte = $gigabyte * 1024;

        
        if (($bytes >= 0) && ($bytes < $kilobyte)) {return $bytes . ' B';} 
        elseif (($bytes >= $kilobyte) && ($bytes < $megabyte)) {return round($bytes / $kilobyte, $precision) . ' Kb';}
        elseif (($bytes >= $megabyte) && ($bytes < $gigabyte)) {return round($bytes / $megabyte, $precision) . ' Mb';}
        elseif (($bytes >= $gigabyte) && ($bytes < $terabyte)) {return round($bytes / $gigabyte, $precision) . ' Gb';}
        elseif ($bytes >= $terabyte) {return round($bytes / $terabyte, $precision) . ' Tb';} 
        else {return $bytes . ' B';}
    }    


?>
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="icon" href="/favicon.ico">
		<title><?=htmlspecialchars($q)?></title>
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
		<style type="text/css">
			body
			{
			padding-top: 80px;
			padding-bottom: 20px;
			}
		</style>
	</head>
	<body>
		<nav class="navbar navbar-default navbar-fixed-top">
			<div class="container">
				<div class="navbar-header">
					<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
					<span class="sr-only">Навигация</span>
					<span class="icon-bar"></span>
					<span class="icon-bar"></span>
					<span class="icon-bar"></span>
					</button>
					<a class="navbar-brand" href="/">Зеркало раздач RuTracker</a>
				</div>
				<div id="navbar" class="navbar-collapse collapse">
					<form action="/search.php" method="GET" class="navbar-form navbar-left">
						<div class="form-group input-group">
							<input type="text" placeholder="" value="<?=htmlspecialchars($q)?>" class="form-control" name="q"> 
							<span class="input-group-btn">
							<button class="btn btn-primary" type="submit"><i class="glyphicon glyphicon-search"></i></button>
							</span>  
						</div>
					</form>
				</div>
				<!--/.navbar-collapse -->
			</div>
		</nav>
		<div class="container">
			<h1><?=htmlspecialchars($q)?></h1>
			<table class="table table-striped">
				<caption>Всего найдено: <?=$torrents[total_found]?></caption>
				<tbody>
					<?
						//Преобразовывем ключи в полученном массиве результатов поиска в массив
					    $ids = array_keys($torrents[matches]);


					    //Собираем массив с id-ми записей в понятный для SQL формат
					    $ids = implode(',', $ids);


					    //Пишем SQL запрос для выборки данных по результатам поиска
					    $sql="SELECT 
						    	`id`,
						    	`name`,
						    	`hash`,
						    	`date`,
						    	`size` 
					    	FROM `torrents` 
					    	WHERE `id` IN (".$ids.") ORDER BY FIELD(`id`, ".$ids.")";


					    //Выполняем SQL запрос
					    $r=mysql_query($sql);


					    //Выводим найденные раздачи
						for ($i=0; $i < mysql_num_rows($r); $i++) 
						{ 
							//Переводим ряд результата запроса в массив
							$f=mysql_fetch_array($r);


							//Переводим дату в русский формат
							$torrent_date=explode('-', $f[date]);
							//Можно просто развернуть массив, но для наглядности сделаем так
							$torrent_date=$torrent_date[2].'.'.$torrent_date[1].'.'.$torrent_date[0];
					?>
					<tr>
						<td width="75%"><a href="/torrent.php?id=<?=$f[id]?>"><?=$f[name]?></a></td>
						<td width="5%"><a href="magnet:?xt=urn:btih:<?=$f[hash]?>"><i class="glyphicon glyphicon-magnet"></i></a></td>
						<td width="10%"><?=bytesToSize($f[size])?></td>
						<td width="10%"><?=$torrent_date?></td>
					</tr>
					<?
						}
					?>
				</tbody>
			</table>
		</div>
		<!-- /.container -->
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
		<script src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script>
	</body>
</html>


Для удобства пользователей, сделаем отдельную страницу для каждого торрента. Вдруг кому-то понадобится отправить ссылку.

Файл torrent.php
<?
//Фильтруем и форматируем id торрента
$id=trim(urldecode($_GET[id]));


//Если нет id, то делаем редирект на главную страницу
if (empty($id)) {header("Location: /"); exit();}


//Если id есть, то...
//Подключаемся к MySQL, при неудаче выводим ошибку
mysql_connect("localhost", "torrent", "password") or die("Could not connect to MySQL");


//Выбираем БД, при неудаче выводим ошибку
mysql_select_db("torrent") or die("Could not select database");


//Переводим все общение с БД в кодировку utf8
mysql_query("SET NAMES utf8");


//Пишем SQL запрос для выборки торрента по id
$sql="SELECT * FROM `torrents` WHERE `id`='".mysql_real_escape_string($id)."'";


//Выполняем SQL запрос
$r=mysql_query($sql);


//Если нет такого id в базе, то делаем редирект на главную страницу
if (mysql_num_rows($r)==0) {header("Location: /"); exit();}


//Переводим ряд результата в массив
$torrent=mysql_fetch_array($r);


//Переводим дату в русский формат
$torrent_date=explode('-', $torrent[date]);
$torrent_date=$torrent_date[2].'.'.$torrent_date[1].'.'.$torrent_date[0];



//Функция перевода байтов в килобайты, мегабайты и тд. Пригодится нам ниже. Описывать её прицип не имеем смысла - чистая арифметика. 
function bytesToSize($bytes, $precision = 0)
{   
        $kilobyte = 1024;
        $megabyte = $kilobyte * 1024;
        $gigabyte = $megabyte * 1024;
        $terabyte = $gigabyte * 1024;

        
        if (($bytes >= 0) && ($bytes < $kilobyte)) {return $bytes . ' B';} 
        elseif (($bytes >= $kilobyte) && ($bytes < $megabyte)) {return round($bytes / $kilobyte, $precision) . ' Kb';}
        elseif (($bytes >= $megabyte) && ($bytes < $gigabyte)) {return round($bytes / $megabyte, $precision) . ' Mb';}
        elseif (($bytes >= $gigabyte) && ($bytes < $terabyte)) {return round($bytes / $gigabyte, $precision) . ' Gb';}
        elseif ($bytes >= $terabyte) {return round($bytes / $terabyte, $precision) . ' Tb';} 
        else {return $bytes . ' B';}
    }    


?>
<!DOCTYPE html>
<html lang="en">
	<head>
		<meta charset="utf-8">
		<meta http-equiv="X-UA-Compatible" content="IE=edge">
		<meta name="viewport" content="width=device-width, initial-scale=1">
		<link rel="icon" href="/favicon.ico">
		<title><?=htmlspecialchars($torrent[name])?></title>
		<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" integrity="sha384-1q8mTJOASx8j1Au+a5WDVnPi2lkFfwwEAa8hDDdjZlpLegxhjVME1fgjWPGmkzs7" crossorigin="anonymous">
		<style type="text/css">
			body
			{
			padding-top: 80px;
			padding-bottom: 20px;
			}
		</style>
	</head>
	<body>
		<nav class="navbar navbar-default navbar-fixed-top">
			<div class="container">
				<div class="navbar-header">
					<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
					<span class="sr-only">Навигация</span>
					<span class="icon-bar"></span>
					<span class="icon-bar"></span>
					<span class="icon-bar"></span>
					</button>
					<a class="navbar-brand" href="/">Зеркало раздач RuTracker</a>
				</div>
				<div id="navbar" class="navbar-collapse collapse">
					<form action="/search.php" method="GET" class="navbar-form navbar-left">
						<div class="form-group input-group">
							<input type="text" placeholder="" value="" class="form-control" name="q"> 
							<span class="input-group-btn">
							<button class="btn btn-primary" type="submit"><i class="glyphicon glyphicon-search"></i></button>
							</span>  
						</div>
					</form>
				</div>
				<!--/.navbar-collapse -->
			</div>
		</nav>
		<div class="container">
			<h1><?=htmlspecialchars($torrent[name])?></h1>
			<table class="table table-striped">
				<tbody>
					<tr>
						<th width="20%">Скачать:</th>
						<td><a href="magnet:?xt=urn:btih:<?=$torrent[hash]?>"><i class="glyphicon glyphicon-magnet"></i> Magnet</a></td>
					</tr>				
					<tr>
						<th width="20%">Размер:</th>
						<td><?=bytesToSize($torrent[size])?></td>
					</tr>
					<tr>
						<th width="20%">Дата раздачи:</th>
						<td><?=$torrent_date?></td>
					</tr>
					<tr>
						<th width="20%">Раздел:</th>
						<td><a target=_blank href="http://rutracker.org/forum/viewforum.php?f=<?=$torrent[cat_id]?>"><?=htmlspecialchars($torrent[cat_name])?></a></td>
					</tr>
					<tr>
						<th width="20%">Обсуждение:</th>
						<td><a target=_blank href="http://rutracker.org/forum/viewtopic.php?t=<?=$torrent[topic_id]?>">Топик #<?=$torrent[topic_id]?></a></td>
					</tr>															
				</tbody>
			</table>
		</div>
		<!-- /.container -->
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
		<script src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script>
	</body>
</html>


Вот и всё. Мы получили полностью рабочий сайт с базой данных от RuTracker, с быстрым поиском и удобным интерфейсом. Я специально не стал добавлять фильтрацию поиска по категориям, сортировку, пагинацию и т.д, что бы был максимально чистый код с самым необходимым. Если будет интерес, я расскажу об этом всем в комментариях или в отдельной статье.

Всем большое спасибо за внимание. Пишите вопросы, всем отвечу.

Комментарии (37)


  1. Lolka
    23.12.2015 10:20

    Поиск работает шустро и все действительно просто я понятно. Не могу сказать, что «зеркало раздач» это до конца верное название, ведь это просто поиск. Если трекер перестанет работать то и многие раздачи просто растеряют сидов через какое-то время.

    Эх, проходят года, а на PHP можно написать все так-же, как когда-то.


  1. ragimovich
    23.12.2015 11:22
    +23

    mysql_connect("localhost", "torrent", "password") or die("Could not connect to MySQL");
    


    Время остановилось. Шел 2015 год. В репозиториях лежал PHP7. А на самом посещаемом IT ресурсе рунета продолжали писать mysql_connect и делать из кода лапшу.


    1. MetaDone
      23.12.2015 11:57
      +13

      просто статья лежала на модерации лет 5


      1. WebSpider
        23.12.2015 13:22
        +5

        Вы хотели сказать 15?


    1. lapopator
      24.12.2015 10:32

      Я запускаю много сайтов на различных серверах и нигде не встречал PHP7. Конечно, может быть мои сервера старые.


      1. ragimovich
        24.12.2015 20:59
        +1

        mysql_connect в статусе deprecated начиная с версии PHP 5.5, если мне память не изменяет. Актуальная версия пятой ветки PHP 5.6. Не заметить предупреждения можно только если везде ставить error_reporting(0), что при разработке/тестах очень странно. Или вы из принципа используете устаревшие функции в надежде на то, что ни один из ваших серверов никогда не обновится до актуальной версии?


    1. aik
      24.12.2015 11:54
      -2

      # apt-cache search php5 | wc -l
      100
      # apt-cache search php6 | wc -l
      0
      # apt-cache search php7 | wc -l
      0

      Debian Stable.


      1. MetaDone
        24.12.2015 12:12
        +1

        1. aik
          24.12.2015 12:18
          -2

          Это комментарий к «В репозиториях лежал PHP7».

          наверно и на дебиане заведется

          Может быть, зачем только? Чтобы весь старый софт переписывать?


        1. ragimovich
          24.12.2015 20:53
          +1

          Для дебиана есть отличный репозиторий обновлений связанных с web пакетов — http://dotdeb.org/


      1. onthefly
        24.12.2015 18:36
        +2

        # apt-cache search php6 | wc -l
        l0l


  1. kovalevsky
    23.12.2015 11:38
    +4

    Автор, а Вы в курсе, что расширение mysql и функции mysql_* deprecated c версии 5.5 и будут удалены в 7?


    1. ragimovich
      23.12.2015 11:49
      +2

      Ну, можно еще добавить, что Sphinx давно не требует специальных библиотек, а позволяет запрашивать данные по MySQL интерфейсу. Да, некоторый функционал через него пару лет назад не работал (сложная агрегация), но то, что у автора в коде, работало уже тогда.

      Но вообще, больше всего меня порадовал вот этот момент

      Открываем браузер, открываем url «http://site.ru/db/insert_to_db.php?f=category_10.csv». Проделываем тоже самое с каждым файлом CSV. Да, все это можно было автоматизировать, но я специально написал так, что бы было максимально всё понятно.

      36 файлов. Таймаут в скрипте установлен на 3 минуты/файл. Итого, больше 1.5 часов перед монитором, вручную перебирая цифры. У меня ощущение, что автор не дошел до главы, где объясняют, как в PHP можно получить список файлов в директории, поэтому списал этот момент на «усложнение кода».


      1. Reuniko
        23.12.2015 13:44
        -1

        В некоторых случаях оптимальней идти напролом, нежели придумывать элегантные решения. Я к таким случаям отношу однократную установку или настройку. Безусловно автор мог создать академический вылизанный код, однократно выполняемый при установке, но посчитал это лишней тратой времени.


        1. ragimovich
          23.12.2015 14:06
          +4

          Причём здесь «академичность»? Мне кажется, тут есть смысл говорить не об академичности, а о банальном знакомстве автора с возможностями выбранных инструментов. Или вы считаете, что тратить 1.5 часа собственного времени на заполнение базы в полуавтоматическом режиме это нормально? При том, что можно это автоматизировать буквально тремя строчками кода. Не двумястами, не пятьюстами, а тремя. Единственный вариант, когда такое может быть оправдано, это если 1.5 часа твоего времени стоят меньше, чем набор трёх строк кода.


          1. ivvi
            23.12.2015 14:19
            -2

            О том, что можно запустить несколько параллельных потоков

            http://site.ru/db/insert_to_db.php?f=category_*.csv
            в разных окнах/табах, не подумал?


            1. Xu4
              24.12.2015 16:53
              +1

              А зачем эти сложности? За 10 минут можно такой скрипт накатать:

              <?php
              function println($message) {
                  echo $message, PHP_EOL;
              }
              
              mb_internal_encoding('UTF-8');
              
              $db = new PDO('mysql:host=localhost;port=3306;dbname=torrent;charset=utf8', 'torrent', 'password', [
                  PDO::ATTR_PERSISTENT => 1,
                  PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                  PDO::ATTR_EMULATE_PREPARES => 0,
              ]);
              
              foreach (glob('csv_data/*.csv') as $file) {
                  $fp = fopen($file, 'r');
              
                  if (!is_resource($fp)) {
                      println("Can't open $file");
                      continue;
                  }
              
                  $counter = 0;
              
                  while (($data = fgetcsv($fp, 0, ';')) !== false) {
                      if (count($data) == 0 && is_null($data[0])) {
                          continue;
                      }
              
                      $db->prepare('INSERT INTO `torrents` (`name`,`hash`,`date`,`size`,`topic_id`,`cat_id`,`cat_name`) VALUES (?,?,?,?,?,?,?)')->execute([$data[4], $data[3], $data[6], $data[5], $data[2], $data[0], $data[1]]);
              
                      $counter++;
                  }
              
                  println("Processed $counter entries from $file");
              
                  fclose($fp);
              }
              
              println('Done');
              


              Его можно дополнительно адаптировать к запуску из web (добавив set_time_limit и заменив PHP_EOL на <br>) — секунд за 20 это можно сделать. А так, он уже готов из консоли работать.


              1. Xu4
                25.12.2015 17:57

                count($data) == 0

                Я имел ввиду: count($data) == 1 — опечатался. Когда fgetcsv встречает пустую строку, он делает массив: [null].


            1. Xu4
              24.12.2015 17:10

              Опять же, в MySQL можно напрямую загружать CSV-файлы — вообще тогда никаких скриптов не нужно писать.


          1. Reuniko
            23.12.2015 15:08

            К сожалению не понимаю математики про 1.5 часа, таймаут не равен времени исполнения, плюс можно запустить несколько параллельных потоков.
            А в реальной жизни бывает так что три строчки кода стоят дороже полного рабочего дня.


      1. ivvi
        23.12.2015 14:15
        +1

        Автоматизировать и обвешивать плюшками можно до бесконечности.
        В этом случае статья заняла бы много экранов, а читатели начали бы зевать уже на файле insert_to_db.php.
        Гораздо важнее изложить базовые принципы, пожертовав лишними и отвлекающими внимание плюшками — с чем автор вполне нормально справился (держи пять!).
        Кому не хватает автоматического обхода листинга директории — тот сам себе напишет; и ни к чему это тащить сюда, где выжимки самого главного.

        з.ы. set_time_limit() лишь ограничивает время исполнения, а не устанавливает его в. Так что, скрипт может выполниться и за 10 секунд, если файл небольшой.


      1. storoj
        24.12.2015 01:14
        +1

        можно сообразить про

        curl http://site.ru/db/insert_to_db.php?f=category_{1..36}.csv
        


      1. lapopator
        24.12.2015 10:30

        Я же написал «все это можно было автоматизировать, но я специально написал так, что бы было максимально всё понятно». Или Вы решили проигнорировать это предложение?


  1. lamazavr
    23.12.2015 13:35

    а чего там поменялось с mysql?
    зы не троль, просто непонятно


    1. MetaDone
      23.12.2015 13:43
      +3

      с версии 5.5 расширение mysql считается устаревшим, а в 7 версии его уже не будет. С учетом того, что 7 версия уже вышла, стоит забыть этот пережиток прошлого


  1. huksley
    23.12.2015 18:19

    А как сделать чтобы обновлялось каждый час с RuTracker? Раз в месяц обновления — это очень редко.


    1. ragimovich
      23.12.2015 19:02
      +4

      Подождите пока автор дойдёт до соответствующей главы и выложит наполненный болью и безысходностью код парсера rutracker на регулярных выражениях. Ведь про DOM в его книжке еще не знают. Да и вообще DOM усложняет код — регулярки привычнее.


    1. Mixim333
      24.12.2015 03:14

      Пол-года назад писал на C# для своего проекта поисковик по RuTracker, ссылка на ветку: https://bitbucket.org/Mixim/watcheroffilminfo/branch/RuTrackerSearchEngine — вроде неплохо работал, все руки не доходят закончить весь проект, может поможет?


  1. karguine
    23.12.2015 18:21
    +1

    Зачем велосипеды, если в mysql можно напрямую загрузить csv?


    1. Borro
      23.12.2015 23:27
      +1

      Зачем велосипеды, если в sphinx можно напрямую загрузить csv?


    1. lapopator
      24.12.2015 10:33
      +1

      Век живи, век учись. Спасибо.


  1. Kanedias
    23.12.2015 23:00
    +1

    Просьба. Автор, залейте код на гитхаб и дайте людям оформить баги/предложения/пуллреквесты.


    1. ragimovich
      24.12.2015 00:17
      +2

      VCS усложняют разработку. ZIP — выбор настоящих программистов! Просите архив, потом выложите ссылку на архив со своими правками, там, глядишь, еще кто подтянется.


      1. rhamdeew
        24.12.2015 14:55

        Один мой знакомый примерно так и принимал пул-реквесты к своей CMS =) Он почему то считал что освоить VCS сложнее чем вручную мержить изменения из кучи файликов =)


  1. lapopator
    24.12.2015 10:38

    Модератор зачем-то удалил ссылку на сайт-пример, не понятно для чего это было сделано. Нарушений правил нету. Попробую оставить её снова. Ведь на рабочем примере будет более понятно, как все работает: rutracker.online


  1. Igogo2012
    24.12.2015 14:47
    +2

    Вот из-за такого кода и насмеиваються над PHP
    Подключение в БД копипастом в 3-х разных файлах…
    В трех Карл!!!

    Зачем использовать mysql_connect, когда все давно используют PDO, он и не диприкейтед и безопасней в использовании?!

    Ну а про выборку из базы прямо среди HTML кода, я пожалуй просто промолчу.


  1. brdsoft
    25.12.2015 23:49
    +1

    Главная страница на чистом HTML, без использования PHP

    Тогда зачем файлу расширение php?

    Глянул пример реализации. По запросу «terminator» 951 результат, по «terminator 1991» — 18642. Гениально.