php-io-extensions / kqueue
1:1 PHP bindings of kqueue(2): kqueue(), kevent(), kevent64(), EV_SET(), EV_SET64()
Package info
github.com/php-io-extensions/kqueue
Language:C
Type:php-ext
Ext name:ext-kqueue
pkg:composer/php-io-extensions/kqueue
Requires
- php: >=8.4
Requires (Dev)
- pestphp/pest: ^4.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-29 11:57:57 UTC
README
1:1 PHP bindings of macOS <sys/event.h>, in plain C. Global functions, same names, same argument order as C. No wrapper package, no policy.
Platform: macOS (Darwin). Binds kevent64(), which only macOS has. PHP ≥ 8.4, 64-bit, NTS + ZTS.
Install
PIE:
pie install php-io-extensions/kqueue
Source checkout, Homebrew php@8.4 + php@8.4-zts (or pass PHP binaries as args):
./install-macos.sh ./install-macos.sh /path/to/bin/php
Script builds in temp copy, installs kqueue.so into each PHP's extension_dir, ad-hoc signs it, writes 30-kqueue.ini into its conf.d.
Manual:
phpize && ./configure --enable-kqueue && make && make install
API
function kqueue(): int; function kevent(int $kq, array $changelist, int $nchanges, ?array &$eventlist, int $nevents, ?timespec $timeout): int; function kevent64(int $kq, array $changelist, int $nchanges, ?array &$eventlist, int $nevents, int $flags, ?timespec $timeout): int; function EV_SET(kevent $kevp, mixed $ident, int $filter, int $flags, int $fflags, int $data, int $udata): void; function EV_SET64(kevent64_s $kevp, mixed $ident, int $filter, int $flags, int $fflags, int $data, int $udata, int $ext0, int $ext1): void; function kqueue_errno(): int; final class kevent { int $ident, $filter, $flags, $fflags, $data, $udata; } final class kevent64_s { int $ident, $filter, $flags, $fflags, $data, $udata; array $ext = [0, 0]; } final class timespec { int $tv_sec, $tv_nsec; }
Constants: all 87 EVFILT_*, EV_*, NOTE_*, KEVENT_FLAG_* from the SDK header, values taken at compile time, plus errno values kqueue(2) documents: EACCES EBADF EFAULT EINTR EINVAL EMFILE ENFILE ENOENT ENOMEM ESRCH.
$kq = kqueue(); $timer = new kevent(); EV_SET($timer, 1, EVFILT_TIMER, EV_ADD | EV_ONESHOT, 0, 250, 0); $wait = new timespec(); $wait->tv_sec = 1; $n = kevent($kq, [$timer], 1, $events, 8, $wait); // 1, $events[0] is a kevent
Mapping to C
- Structs.
struct kevent,struct kevent64_s,struct timespec→ final classes, typed public props named as C fields, zero-initialised, no dynamic props. Class and function share namekeventas in C; PHP keeps them in separate symbol tables. - Arrays in, arrays out.
changelist= PHP array ofkevent(kevent64_sforkevent64); first$nchangesentries in iteration order are sent.$eventlistreceives list of newkevent/kevent64_s, length = return value. On-1,$eventlistuntouched, as C leaves its buffer. timeout.null= CNULL= block.new timespec()= poll.- Widths.
filter(int16_t),flags(uint16_t),fflagsandkevent64flags(uint32_t) accept either signed or unsigned reading of their width, as C's implicit conversion does. Wider values throwValueError.EV_SET/EV_SET64store the value as the C field holds it:EV_SET($k, 0, 0xffff, -1, NOTE_PCTRLMASK, 0, 0)→filter -1,flags 65535,fflags 0xfff00000. Constants carry C expression values:NOTE_PCTRLMASK=~0x000fffff=-1048576. - ident.
EV_SET/EV_SET64takeint, a stream resource, or aSocket(when ext-sockets is loaded). Stream andSocketresolve to their fd, same asepoll_ctl's$fd; the struct stores the int. Non-fd filters (EVFILT_SIGNAL,EVFILT_PROC,EVFILT_TIMER,EVFILT_USER) pass their int ident as before. Other types throwTypeError; a closed stream throwsTypeError, a stream with no fdValueError. - 64-bit fields.
ident,data,udata,ext[]pass through as bit patterns.udatais pointer-sized int: kernel returns exactly what you set, nothing more. Unsigned 64-bit values ≥ 2^63 read back negative. - errno. PHP runtime can overwrite
errnobefore PHP code runs, so each binding saves it right after the syscall on-1.kqueue_errno()reads it. Reset per request. - Descriptor lifetime.
close(2)is POSIX, not kqueue: usePosi\System::close()fromphp-io-extensions/posi. - Invalid arguments (
nchangesoutsidechangelist, negativenevents, wrong element class, unset prop, malformedext) throwValueError/TypeError/Errorbefore any syscall.
Kernel behaviour passes through unchanged:
- A change that fails while
$nevents > 0comes back as an event withEV_ERRORinflagsand errno indata; with$nevents = 0the call returns-1andkqueue_errno()holds errno.EV_RECEIPTforces the event form. - XNU binds a kqueue to the first ABI it is used with.
kevent64()on a kqueue already used withkevent()(or reverse) returns-1,EINVAL. - Signals interrupt a blocking
kevent():-1,EINTR.
Tests
Pest v4. Needs extension loaded:
composer install
php -d extension=/path/to/kqueue.so vendor/bin/pest # or plain php once installed
Regenerating arginfo
kqueue_arginfo.h is generated from kqueue.stub.php; edit stub, then:
php "$(php-config --prefix)/lib/php/build/gen_stub.php" kqueue.stub.php
License
MIT