С помощью тиков Вы можете сделать вывод статуса прогресса паралельно выполнению основной задаче.
Делается это с помощью следующих функций: register_tick_function и unregister_tick_function. А так же задаётся интервал вызова этих функций в тиках с помощью declare(ticks=N), где N - количество тиков.
Один тик это слишком маленький интервал для того что бы выводить сообщение с прогрессом каждый раз, а тем более сохранять его куда то что бы показать клиенту.
По этому я предлагаю использовать для вывода дополнительные переменные которые будут выполнять нужные операции с определённой задержкой.
Вот простой пример:
<?php // Переменная для эмуляции основной работы $a = 1; // Время последнего вывода статуса $lastUpdate = time(); /* * Для уменьшения нагрузки запускаем функцию каждые 10 000 тиков */ declare(ticks=10000); /** * Функция для вывода прогресса. * Будет выводить данные каждую секунду. */ function outProgress() { global $a, $lastUpdate; if ($lastUpdate != time()) { $lastUpdate = time(); echo ceil($a / 1000) . "%\n"; } } // Регистрируем функцию для вывода прогресса register_tick_function('outProgress'); // Эмулируем сложную задачу while ($a < 100000) { $a++; usleep(100); }
Для более практических целей нам понадобиться класс который будет предоставлять нам обёртку для этих функций, и будет более простой в использование.
<?php class Timer { private $_lastRun; private $_interval; private $_function; private $_params = array(); private $_hasRun = false; function __construct($interval, $function, $arg) { $this->_interval = $interval; $this->_function = $function; if ($arg != null) { $this->_params = array_slice(func_get_args(), 2); } } public function Start() { if ($this->_hasRun) { return; } $this->_lastRun = time(); register_tick_function(array($this, 'Tick')); $this->_hasRun = true; } public function Stop() { if (!$this->_hasRun) { return; } unregister_tick_function(array($this, 'Tick')); $this->_hasRun = false; } public function Tick() { if ($this->_lastRun + $this->_interval < time()) { return; } call_user_func_array($this->_function, $this->_params); $this->_lastRun = time(); } }
А теперь используем его:
<?php include './Timer.php'; // Переменная для эмуляции основной работы $a = 1; /** * Функция для вывода прогресса. * Будет выводить данные каждую секунду. */ function outProgress($param) { global $a; echo $param . " : " . ceil($a / 1000) . "%\n"; } $timer = new Timer(1, 'outProgress', 'test'); $timer->Start(); declare(ticks=10000) { // Эмулируем сложную задачу while ($a < 100000) { $a++; usleep(100); if ($a > 50000) { $timer->Stop(); } } }
Как видим, теперь у нас появилось больше возможностей: мы можем запускать и останавливать для нужных нам задач, а так же у нас есть удобный способ указания интервала. Так же у нас сохраняется возможность передачи параметров в функцию таймера.
Замечание 1
Мы не можем останавливать работу таймера при срабатывание самого таймера.
function outProgress($param) { global $a; echo $param . " : " . ceil($a / 1000) . "%\n"; if ($a > 50000) { $timer->Stop(); //Выдаст предупреждение и таймер НЕ ОСТАНОВИТЬСЯ. } }
Замечание 2
declare(ticks=N) необходимо объявлять в том месте где у вас будет выполняться код, ход выполнения которого вы хотите отслеживать. Если объявить его в классе Timer то результата не будет.
Автор: Сергей Степанов
Поделиться @