Примеры

Пример #1 Простые таймеры

<?php
// Создаем и запускаем таймер на 2 секунды
$w1 = new EvTimer(20, function () {
    echo 
"2 секунды прошло\n";
});

// Создаем и запускаем таймер, который сработает через 2 секунды, после чего будет срабатывать
// раз в секунду, пока вы его вручную не остановите
$w2 = new EvTimer(21, function ($w) {
    echo 
"вызывается раз в секунду, первое срабатывание через 2 секунды\n";
    echo 
"итерация = "Ev::iteration(), PHP_EOL;

    
// Останавливаем наблюдателя через 5 итераций
    
Ev::iteration() == and $w->stop();
    
// Остановливаем наблюдателя, если следующий вызов приведет к десятой (или больше) итерации
    
Ev::iteration() >= 10 and $w->stop();
});

// создаем остановленный таймер. Он будет неактивен, пока мы его не запустим
$w_stopped EvTimer::createStopped(105, function($w) {
    echo 
"Callback-функция таймера, созданного остановленным\n";

    
// Останавливаем наблюдателя через 2 итерации
    
Ev::iteration() >= and $w->stop();
});

// Запускаем событийный цикл, пока работает хотя бы один наблюдатель или пока не вызван Ev::stop()
Ev::run();

// Запускаем и смотрим, как он работает
$w_stopped->start();
echo 
"Запускаем одну итерацию\n";
Ev::run(Ev::RUN_ONCE);

echo 
"Перезапускаем второго наблюдателя и пытаемся отловить те же события, но не блокируем\n";
$w2->again();
Ev::run(Ev::RUN_NOWAIT);

$w = new EvTimer(100, function() {});
echo 
"Запускаем блокирующий цикл\n";
Ev::run();
echo 
"END\n";
?>

Результатом выполнения данного примера будет что-то подобное:

2 секунды прошло
вызывается раз в секунду, первое срабатывание через 2 секунды
итерация = 1
вызывается раз в секунду, первое срабатывание через 2 секунды
итерация = 2
вызывается раз в секунду, первое срабатывание через 2 секунды
итерация = 3
вызывается раз в секунду, первое срабатывание через 2 секунды
итерация = 4
вызывается раз в секунду, первое срабатывание через 2 секунды
итерация = 5
Запускаем одну итерацию
Функция обратного вызова таймера, созданного остановленным
Перезапускаем второго наблюдателя и пытаемся отловить те же события, но не блокируем
Запускаем блокирующий цикл
вызывается раз в секунду, первое срабатывание через 2 секунды
итерация = 8
вызывается раз в секунду, первое срабатывание через 2 секунды
итерация = 9
вызывается раз в секунду, первое срабатывание через 2 секунды
итерация = 10
END

Пример #2 Периодический таймер. Срабатывает раз в 10.5 секунд

<?php
$w 
= new EvPeriodic(0.10.5NULL, function ($w$revents) {
    echo 
time(), PHP_EOL;
});

Ev::run();
?>

Пример #3 Периодический таймер. Использование callback-функции для перезадания интервала

<?php
// Срабатывает раз в 10.5 секунд

function reschedule_cb ($watcher$now) {
    return 
$now + (10.5. - fmod($now10.5));
}

$w = new EvPeriodic(0.0."reschedule_cb", function ($w$revents) {
    echo 
time(), PHP_EOL;
});

Ev::run();
?>

Пример #4 Периодический таймер. Срабатывает каждые 10.5 секунд, начиная с текущего момента

<?php
// Срабатывает раз в 10.5 секунд начиная с текущего момента
$w = new EvPeriodic(fmod(Ev::now(), 10.5), 10.5NULL, function ($w$revents) {
    echo 
time(), PHP_EOL;
});

Ev::run();
?>

Пример #5 Ждем, пока STDIN не станет читаемым

<?php
// Ждем, пока STDIN не станет читаемым
$w = new EvIo(STDINEv::READ, function ($watcher$revents) {
    echo 
"STDIN is readable\n";
});

Ev::run(Ev::RUN_ONCE);
?>

Пример #6 Используем асинхронный ввод/вывод для доступа к сокету

<?php
/* Используем асинхронный ввод/вывод для доступа к сокету */

// расширение `sockets' продолжит логировать предупреждения
// для EINPROGRESS, EAGAIN/EWOULDBLOCK etc.
error_reporting(E_ERROR);

$e_nonblocking = array (/*EAGAIN или EWOULDBLOCK*/11/*EINPROGRESS*/115);

// Получаем порт для сервиса WWW
$service_port getservbyname('www''tcp');

// Получаем IP-адрес целевого хоста
$address gethostbyname('google.co.uk');

// Создаем сокет TCP/IP
$socket socket_create(AF_INETSOCK_STREAMSOL_TCP);
if (
$socket === FALSE) {
    echo 
"Ошибка при вызове socket_create(): причина: "
        
.socket_strerror(socket_last_error()) . "\n";
}

// Устанавливаем флаг O_NONBLOCK
socket_set_nonblock($socket);

// Прерываем по таймауту
$timeout_watcher = new EvTimer(10.00., function () use ($socket) {
    
socket_close($socket);
    
Ev::stop(Ev::BREAK_ALL);
});

// Посылаем запрос HEAD когда сокет доступен для записи
$write_watcher = new EvIo($socketEv::WRITE, function ($w)
    use (
$socket$timeout_watcher$e_nonblocking)
{
    
// Останавливаем наблюдателя $timeout_watcher
    
$timeout_watcher->stop();
    
// Останавливаем наблюдателя $write_watcher
    
$w->stop();

    
$in "HEAD / HTTP/1.1\r\n";
    
$in .= "Host: google.co.uk\r\n";
    
$in .= "Connection: Close\r\n\r\n";

    if (!
socket_write($socket$instrlen($in))) {
        
trigger_error("Ошибка записи $in в сокет"E_USER_ERROR);
    }

    
$read_watcher = new EvIo($socketEv::READ, function ($w$re)
        use (
$socket$e_nonblocking)
    {
        
// Сокет доступен для чтения. Читаем 20 байт в неблокирующем режиме
        
$ret socket_recv($socket$out20MSG_DONTWAIT);

        if (
$ret) {
            echo 
$out;
        } elseif (
$ret === 0) {
            
// Все прочтено
            
$w->stop();
            
socket_close($socket);
            return;
        }

        
// Ловим EINPROGRESS, EAGAIN или EWOULDBLOCK
        
if (in_array(socket_last_error(), $e_nonblocking)) {
            return;
        }

        
$w->stop();
        
socket_close($socket);
    });

    
Ev::run();
});

$result socket_connect($socket$address$service_port);

Ev::run();
?>

Результатом выполнения данного примера будет что-то подобное:

HTTP/1.1 301 Moved Permanently
Location: http://www.google.co.uk/
Content-Type: text/html; charset=UTF-8
Date: Sun, 23 Dec 2012 16:08:27 GMT
Expires: Tue, 22 Jan 2013 16:08:27 GMT
Cache-Control: public, max-age=2592000
Server: gws
Content-Length: 221
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Connection: close


Пример #7 Встраиваем один цикл в другой

<?php
/*
* Пытаемся получить встраиваемый цикл и встроить его в событийный цикл по умолчанию.
* Если это невозможно - используем цикл по умолчанию. Цикл по умолчанию
* хранится в $loop_hi, а встраиваемый в $loop_lo (который будет равен $loop_hi в случае
* если мы не будем использовать встраиваемый цикл).
*
* Пример взят из
* http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Examples_CONTENT-9
*/
$loop_hi EvLoop::defaultLoop();
$loop_lo NULL;
$embed   NULL;

/*
* Смотрим, есть ли возможность получить работающий
* (Значение 0 означает автоопределение)
*/
$loop_lo Ev::embeddableBackends() & Ev::recommendedBackends()
    ? new 
EvLoop(Ev::embeddableBackends() & Ev::recommendedBackends())
    : 
0;

if (
$loop_lo) {
    
$embed = new EvEmbed($loop_lo, function () {});
} else {
    
$loop_lo $loop_hi;
}
?>

Пример #8 Встраивание цикла, созданного с помощью kqueue в цикл по умолчанию

<?php
/*
* Проверяем, что бэкенд kqueue доступен, но не рекомендован, и создаем его для
* работы с сокетами (которые обычно работают с любой реализацией kqueue).
* Сохраняем событийный цикл kqueue/socket-only в loop_socket. (Можно опционально
* использовать флаг EVFLAG_NOENV)
*
* Пример взят из
* http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Examples_CONTENT-9
*/
$loop        EvLoop::defaultLoop();
$socket_loop NULL;
$embed       NULL;

if (
Ev::supportedBackends() & ~Ev::recommendedBackends() & Ev::BACKEND_KQUEUE) {
    if ((
$socket_loop = new EvLoop(Ev::BACKEND_KQUEUE))) {
        
$embed = new EvEmbed($loop);
    }
}

if (!
$socket_loop) {
    
$socket_loop $loop;
}

// теперь используем $socket_loop для всех сокетов, а $loop для всего остального
?>

Пример #9 Перехватываем сигнал SIGTERM

<?php
$w 
= new EvSignal(SIGTERM, function ($watcher) {
    echo 
"Получен сигнал SIGTERM\n";
    
$watcher->stop();
});

Ev::run();
?>

Пример #10 Отслеживаем изменение /var/log/messages

<?php
// Используем интервал опроса в 10 секунд
$w = new EvStat("/var/log/messages"8, function ($w) {
    echo 
"/var/log/messages изменен\n";

    
$attr $w->attr();

    if (
$attr['nlink']) {
        
printf("Текущий размер: %ld\n"$attr['size']);
        
printf("Текущее значение atime: %ld\n"$attr['atime']);
        
printf("Текущее значение mtime: %ld\n"$attr['mtime']);
    } else {
        
fprintf(STDERR"файл `messages` отсутствует!");
        
$w->stop();
    }
});

Ev::run();
?>

Пример #11 Отслеживаем изменение /var/log/messages. Избегаем пропуска обновлений с помощью задержки в одну секунду

<?php
$timer 
EvTimer::createStopped(0.1.02, function ($w) {
    
$w->stop();

    
$stat $w->data;

    
// 1 секунду после последнего изменения файла
    
printf("Текущий размер: %ld\n"$stat->attr()['size']);
});

$stat = new EvStat("/var/log/messages"0., function () use ($timer) {
    
// Сбрасываем наблюдателя $timer
    
$timer->again();
});

$timer->data $stat;

Ev::run();
?>

Пример #12 Отслеживаем изменения статуса процесса

<?php
$pid 
pcntl_fork();

if (
$pid == -1) {
    
fprintf(STDERR"pcntl_fork failed\n");
} elseif (
$pid) {
    
$w = new EvChild($pidFALSE, function ($w$revents) {
        
$w->stop();

        
printf("Процесс %d вышел с кодом %d\n"$w->rpid$w->rstatus);
    });

    
Ev::run();

    
// Защита от зомби процессов
    
pcntl_wait($status);
} else {
    
// Порожденный потомок
    
exit(2);
}
?>