С помощью тиков Вы можете сделать вывод статуса прогресса паралельно выполнению основной задаче.
Делается это с помощью следующих функций: 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 то результата не будет.
Автор: Сергей Степанов
Поделиться @