Мастерская iPloGic
+7 (926) 961-66-26

 — База знаний — Пишем циклический скрипт на PHP без ошибки циклической переадресации

Пишем циклический скрипт на PHP без ошибки циклической переадресации

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

Ну общий подход понятен. Делаем цикл и в нем поочередно обрабатываем файлы или записи таблиц. И тут оказывается, что обрабатываемой информации уж ооочень много. Сервер не справляется. То время ожидания истекает, то памяти не хватает. Оптимизируем по максимуму, убираем все лишнее из память минимизируем количество сношений с базой... Результат нулевой.

"Умники" с форумов в таких случаях начинают советовать увеличить время ожидания сервера, взять сервер помощнее ну и т.п. Но ведь не всегда можно увеличить время ожидания, хостер не позволяет или скрипт должен быть универсальным. Ну а про смену сервера я вообще говорить не буду.

Вот тут на арену и выходит циклический скрипт.

Идея, в принципе, проста. Выполнение всего скрипта разбивается на шаги. Каждый шаг выполняется после перезапуска скрипта и содержит такое количество циклов обработки информации, которое гарантированно будет выполнено сервером. Другими словами, часть обработки, перезапустили скрипт, обработали следующую порцию информации и так далее, пока вся информация не будет обработана. При этом при каждом перезапуске скрипта память и время выполнения обнуляется.

Теперь от теории к практике.

Файл скрипта строится примерно так: вычисление параметров шага -> обработка порции данных -> перезапуск этого же скрипта с новыми параметрами шага.

Пример такого файла, назовем его script.php.

<? 
// определяем параметры шага
$step_size = 500;                 //  количество единиц информации, обрабатываемых за шаг
$step=$_GET['step'];              //  определяем номер шага, для примера мы его передаем через GET
if ($step=='') { $step=1; }       //  если шаг не задан, то 1
$start = ($step-1)*$step_size;    //  начальная позиция информации для обработки 
$newstep = $step+1;               //  следующий шаг
/*  обработка $step_size единиц информации, начиная с позиции $start   */
// переадресация на самого себя с новыми параметрами
header("Location: script.php?step=".$newstep." ");
?>


Все? А вот и нет! Если у нас будет достаточно большое количество итераций, то на каком то шаге, который зависит от браузера и его настроек (обычно это 2-5) мы увидем что-то вроде

Ошибка 310 (net::ERR_TOO_MANY_REDIRECTS): Обнаружено слишком много переадресаций.

Опаньки! А собственно ничего удивительного. Большинство современных браузеров оснацены защитой от циклической переадресации. Если на сайте есть ошибка переадресации, то он может бесконечно загружать сам себя, что, как Вы понимаете, ни к чему хорошему не приведет. Вот и ввели эту защиту. Браузер же не знает, что наша переадресация "правильная".

Как с этим бороться. Есть несколько подходов. Если есть возможность "командовать" пользователями или только Вы сами будете им пользоваться, то возьмите браузер где этой защиты нет или ее можно отключить. Проблема снята.

Чаще же всего Вы не знаете кто и в чем будет запускать Ваш скрипт. В этом случае мы предлагаем самый надежный способ. Для переадресации мы используем javascript браузера клиента.

script.php

<?
// определяем параметры шага
$step_size = 500;                      //  количество единиц информации, обрабатываемых за шаг
$step=$_GET['step'];                   //  определяем номер шага, для примера мы его передаем через GET
if ($step=='') { $step=1; }            //  если шаг не задан, то 1
$start = ($step-1)*$step_size;         //  начальная позиция информации для обработки 
$newstep = $step+1;                    //  следующий шаг
/*  обработка $step_size единиц информации, начиная с позиции $start   */
// переадресация на самого себя с новыми параметрами
echo('<html>
<head>
<title>Обновление</title>
</head>
<body>
Идет обновление.<br /><a id="go" href="script.php?step='.$newstep.'">.</a>
<pre class="redactor-script-tag" style="display: none;"  type="text/javascript">document.getElementById(\'go\').click();</pre>
</body>
</html>');
die();
?>


Вот такая Конструкция уже будет работать. Браузер сам инициирует переход на нужную нам страницу, при этом не используется не одна функция автоматической переадресации.

Еще одна проблема при использовании циклических скриптов может возникнуть, если Вы запускаете скрипт не из браузера. Например, используете для этого cron. Предыдущие методы нам не помогут, так как и header, и уж тем более javascript не работают без браузера. Для этих целей можно использовать функцию exec(). Эта функция запускает скрипт без отменения выполнения текущего. Подробно здесь мы его описывать не будем.

<? 
// определяем параметры шага
$step_size = 500;                      //  количество единиц информации, обрабатываемых за шаг
$step=$_GET['step'];                   //  определяем номер шага, для примера мы его передаем через GET
if ($step=='') { $step=1; }            //  если шаг не задан, то 1
$start = ($step-1)*$step_size;         //  начальная позиция информации для обработки 
$newstep = $step+1;                    //  следующий шаг
/*  обработка $step_size единиц информации, начиная с позиции $start   */
// переадресация на самого себя с новыми параметрами
exec('wget -b -q -O temp.php http://www.site.ru/script.php');
?>


Использовать последний метод можно и из браузера, но в этом случае пользователь не сможет наблюдать за ходом процесса и узнать о его завершении.

Вот собственно и все. Надеемся Вам поможет эта статья.

13.10.2012