README.md
1ev PECL extension
2
3DESCRIPTION
4===========
5
6ev is a PECL extension providing interface to libev library - high performance
7full-featured event loop written in C.
8
9
10ABOUT LIBEV
11-----------
12
13Libev is an event loop: you register interest in certain events (such as a file
14descriptor being readable or a timeout occurring), and it will manage these
15event sources and provide your program with events.
16
17To do this, it must take more or less complete control over your process (or
18thread) by executing the event loop handler, and will then communicate events
19via a callback mechanism.
20
21You register interest in certain events by registering so-called event watchers,
22and then hand it over to libev by starting the watcher.
23
24For details refer to the libev's homepage:
25<http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#EMBEDDING>
26
27For installation instructions see file named INSTALL.
28
29
30LIBEV IS EMBEDDED
31-----------------
32
33You don't need to install libev separately, since it is embedded into this
34extension.
35
36
37PORTABILITY
38-----------
39
40Currently GNU/Linux platforms supported only. But likely will work on others
41too.
42
43
44EXAMPLES
45========
46
47SIMPLE TIMERS
48-------------
49
50 <?php
51 // Create and start timer firing after 2 seconds
52 $w1 = new EvTimer(2, 0, function () {
53 echo "2 seconds elapsed\n";
54 });
55
56 // Create and launch timer firing after 2 seconds repeating each second
57 // until we manually stop it
58 $w2 = new EvTimer(2, 1, function ($w) {
59 echo "is called every second, is launched after 2 seconds\n";
60 echo "iteration = ", Ev::iteration(), PHP_EOL;
61
62 // Stop the watcher after 5 iterations
63 Ev::iteration() == 5 and $w->stop();
64 // Stop the watcher if further calls cause more than 10 iterations
65 Ev::iteration() >= 10 and $w->stop();
66 });
67
68 // Create stopped timer. It will be inactive until we start it ourselves
69 $w_stopped = EvTimer::createStopped(10, 5, function($w) {
70 echo "Callback of a timer created as stopped\n";
71
72 // Stop the watcher after 2 iterations
73 Ev::iteration() >= 2 and $w->stop();
74 });
75
76 // Loop until Ev::stop() is called or all of watchers stop
77 Ev::run();
78
79 // Start and look if it works
80 $w_stopped->start();
81 echo "Run single iteration\n";
82 Ev::run(Ev::RUN_ONCE);
83
84 echo "Restart the second watcher and try to handle the same events, but don't block\n";
85 $w2->again();
86 Ev::run(Ev::RUN_NOWAIT);
87
88 $w = new EvTimer(10, 0, function() {});
89 echo "Running a blocking loop\n";
90 Ev::run();
91 echo "END\n";
92 ?>
93
94*Output*
95
96 2 seconds elapsed
97 is called every second, is launched after 2 seconds
98 iteration = 1
99 is called every second, is launched after 2 seconds
100 iteration = 2
101 is called every second, is launched after 2 seconds
102 iteration = 3
103 is called every second, is launched after 2 seconds
104 iteration = 4
105 is called every second, is launched after 2 seconds
106 iteration = 5
107 Run single iteration
108 Callback of a timer created as stopped
109 Restart the second watcher and try to handle the same events, but don't block
110 Running a blocking loop
111 is called every second, is launched after 2 seconds
112 iteration = 8
113 is called every second, is launched after 2 seconds
114 iteration = 9
115 is called every second, is launched after 2 seconds
116 iteration = 10
117 END
118
119PERIODIC TIMERS
120---------------
121
122*Example 1*
123
124 <?php
125 // Tick each 10.5 seconds
126 $w = new EvPeriodic(0., 10.5, NULL, function ($w, $revents) {
127 echo time(), PHP_EOL;
128 });
129 Ev::run();
130 ?>
131
132*Example 2*
133
134 <?php
135 // Tick each 10.5 seconds. Use reschedule callback
136
137 function reschedule_cb ($watcher, $now) {
138 return $now + (10.5. - fmod($now, 10.5));
139
140 }
141
142 $w = new EvPeriodic(0., 0., "reschedule_cb", function ($w, $revents) {
143 echo time(), PHP_EOL;
144 });
145 Ev::run();
146 ?>
147
148*Example 3*
149
150 <?php
151 // Tick every 10.5 seconds starting at now
152 $w = new EvPeriodic(fmod(Ev::now(), 10.5), 10.5, NULL, function ($w, $revents) {
153 echo time(), PHP_EOL;
154 });
155 Ev::run();
156 ?>
157
158I/O EVENTS
159----------
160
161*Example 1*
162
163 <?php
164 // Wait until STDIN is readable
165 $w = new EvIo(STDIN, Ev::READ, function ($watcher, $revents) {
166 echo "STDIN is readable\n";
167 });
168 Ev::run(Ev::RUN_ONCE);
169 ?>
170
171*Example 2*
172
173 <?php
174 /* Use some async I/O to access a socket */
175
176 // `sockets' extension still logs warnings
177 // for EINPROGRESS, EAGAIN/EWOULDBLOCK etc.
178 error_reporting(E_ERROR);
179
180 $e_nonblocking = array (/*EAGAIN or EWOULDBLOCK*/11, /*EINPROGRESS*/115);
181
182 // Get the port for the WWW service
183 $service_port = getservbyname('www', 'tcp');
184
185 // Get the IP address for the target host
186 $address = gethostbyname('google.co.uk');
187
188 // Create a TCP/IP socket
189 $socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
190 if ($socket === FALSE) {
191 echo "socket_create() failed: reason: "
192 .socket_strerror(socket_last_error()) . "\n";
193 }
194
195 // Set O_NONBLOCK flag
196 socket_set_nonblock($socket);
197
198 // Abort on timeout
199 $timeout_watcher = new EvTimer(10.0, 0., function () use ($socket) {
200 socket_close($socket);
201 Ev::stop(Ev::BREAK_ALL);
202 });
203
204 // Make HEAD request when the socket is writable
205 $write_watcher = new EvIo($socket, Ev::WRITE, function ($w)
206 use ($socket, $timeout_watcher, $e_nonblocking) {
207 // Stop timeout watcher
208 $timeout_watcher->stop();
209 // Stop write watcher
210 $w->stop();
211
212 $in = "HEAD / HTTP/1.1\r\n";
213 $in .= "Host: google.co.uk\r\n";
214 $in .= "Connection: Close\r\n\r\n";
215
216 if (!socket_write($socket, $in, strlen($in))) {
217 trigger_error("Failed writing $in to socket", E_USER_ERROR);
218 }
219
220 $read_watcher = new EvIo($socket, Ev::READ, function ($w, $re)
221 use ($socket, $e_nonblocking) {
222 // Socket is readable. recv() 20 bytes using non-blocking mode
223 $ret = socket_recv($socket, $out, 20, MSG_DONTWAIT);
224
225 if ($ret) {
226 echo $out;
227 } elseif ($ret === 0) {
228 // All read
229 $w->stop();
230 socket_close($socket);
231 return;
232 }
233
234 // Caught EINPROGRESS, EAGAIN, or EWOULDBLOCK
235 if (in_array(socket_last_error(), $e_nonblocking)) {
236 return;
237 }
238
239 $w->stop();
240 socket_close($socket);
241 });
242
243 Ev::run();
244 });
245
246 $result = socket_connect($socket, $address, $service_port);
247
248 Ev::run();
249 ?>
250
251Sample output:
252
253 HTTP/1.1 301 Moved Permanently
254 Location: http://www.google.co.uk/
255 Content-Type: text/html; charset=UTF-8
256 Date: Sun, 23 Dec 2012 16:08:27 GMT
257 Expires: Tue, 22 Jan 2012 16:08:27 GMT
258 Cache-Control: public, max-age=2592000
259 Server: gws
260 Content-Length: 221
261 X-XSS-Protection: 1; mode=block
262 X-Frame-Options: SAMEORIGIN
263 Connection: close
264
265
266EMBEDDING ONE LOOP INTO ANOTHER
267-------------------------------
268
269*Example 1*
270
271 <?php
272 /*
273 * Try to get an embeddable event loop and embed it into the default event loop.
274 * If it is impossible, use the default
275 * loop. The default loop is stored in `$loop_hi`, while the embeddable loop is
276 * stored in `$loop_lo`(which is `$loop_hi` in the case no embeddable loop can be
277 * used).
278 *
279 * Sample translated to PHP
280 * <http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Examples_CONTENT-9>
281 */
282 $loop_hi = EvLoop::defaultLoop();
283 $loop_lo = NULL;
284 $embed = NULL;
285
286 /*
287 * See if there is a chance of getting one that works
288 * (flags' value of 0 means autodetection)
289 */
290 $loop_lo = Ev::embeddableBackends() & Ev::recommendedBackends()
291 ? new EvLoop(Ev::embeddableBackends() & Ev::recommendedBackends())
292 : 0;
293
294 if ($loop_lo) {
295 $embed = new EvEmbed($loop_lo, function () {});
296 } else {
297 $loop_lo = $loop_hi;
298 }
299 ?>
300
301*Example 2*
302
303 <?php
304 /*
305 * Check if kqueue is available but not recommended and create a kqueue backend
306 * for use with sockets (which usually work with any kqueue implementation).
307 * Store the kqueue/socket-only event loop in `$socket_loop`. (One might optionally
308 * use `EVFLAG_NOENV`, too)
309 *
310 * Example borrowed from
311 * <http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod#Examples_CONTENT-9>
312 */
313 $loop = EvLoop::defaultLoop();
314 $socket_loop = NULL;
315 $embed = NULL;
316
317 if (Ev::supportedBackends() & ~Ev::recommendedBackends() & Ev::BACKEND_KQUEUE) {
318 if (($socket_loop = new EvLoop(Ev::BACKEND_KQUEUE))) {
319 $embed = new EvEmbed($loop);
320 }
321 }
322
323 if (!$socket_loop) {
324 $socket_loop = $loop;
325 }
326
327 // Now use $socket_loop for all sockets, and $loop for anything else
328 ?>
329
330SIGNALS
331-------
332
333 <?php
334 // Handle SIGTERM signal
335 $w = new EvSignal(SIGTERM, function ($watcher) {
336 echo "SIGTERM received\n";
337 $watcher->stop();
338 });
339 Ev::run();
340 ?>
341
342STAT - FILE STATUS CHANGES
343--------------------------
344
345*Example 1*
346
347 <?php
348 // Monitor changes of /var/log/messages.
349 // Use 10 second update interval.
350 $w = new EvStat("/var/log/messages", 8, function ($w) {
351 echo "/var/log/messages changed\n";
352
353 $attr = $w->attr();
354
355 if ($attr['nlink']) {
356 printf("Current size: %ld\n", $attr['size']);
357 printf("Current atime: %ld\n", $attr['atime']);
358 printf("Current mtime: %ld\n", $attr['mtime']);
359 } else {
360 fprintf(STDERR, "`messages` file is not there!");
361 $w->stop();
362 }
363 });
364
365 Ev::run();
366 ?>
367
368*Example 2*
369
370 <?php
371 // Avoid missing updates by means of one second delay
372 $timer = EvTimer::createStopped(0., 1.02, function ($w) {
373 $w->stop();
374
375 $stat = $w->data;
376
377 // 1 second after the most recent change of the file
378 printf("Current size: %ld\n", $stat->attr()['size']);
379 });
380
381 $stat = new EvStat("/var/log/messages", 0., function () use ($timer) {
382 // Reset timer watcher
383 $timer->again();
384 });
385
386 $timer->data = $stat;
387
388 Ev::run();
389 ?>
390
391PROCESS STATUS CHANGES
392----------------------
393
394 <?php
395 $pid = pcntl_fork();
396
397 if ($pid == -1) {
398 fprintf(STDERR, "pcntl_fork failed\n");
399 } elseif ($pid) {
400 $w = new EvChild($pid, FALSE, function ($w, $revents) {
401 $w->stop();
402
403 printf("Process %d exited with status %d\n", $w->rpid, $w->rstatus);
404 });
405
406 Ev::run();
407
408 // Protect against Zombies
409 pcntl_wait($status);
410 } else {
411 //Forked child
412 exit(2);
413 }
414 ?>
415
416
417AUTHORS
418=======
419
420Ruslan Osmanov <osmanov@php.net>
421
422
423COPYRIGHT
424=========
425
426 Copyright (c) 2012-2019 Ruslan Osmanov <osmanov@php.net>
427
428 This project is subject to version 3.01 of the PHP license, that is bundled
429 with this package in the file LICENSE, and is available through the
430 world-wide-web at the following url: http://www.php.net/license/3_01.txt If you
431 did not receive a copy of the PHP license and are unable to obtain it through
432 the world-wide-web, please send a note to license@php.net so we can mail you a
433 copy immediately.
434
435
436vim: tw=80 ft=markdown
437
438