PHP-controllable UNIX file descriptor extension built with Zephir.
The FD extension allows PHP to open raw UNIX file descriptors, enabling control beyond simple stream-based I/O. The integer descriptor can be passed into other extensions or used directly for system-level interfaces such as GPIO, I2C, SPI, and UART.
- PHP 8.x
- A UNIX-like operating system (Linux, macOS)
- Standard C build tools (
gcc,make, PHP development headers)
PHP PIE is the official PHP extension installer. It handles the full build and activation pipeline automatically.
Install PIE if you haven't already:
curl -Lo pie.phar https://github.com/php/pie/releases/latest/download/pie.phar
chmod +x pie.phar
sudo mv pie.phar /usr/local/bin/pieThen install the extension:
pie install php-io-extensions/fdPIE will build the extension from source, copy the .so into PHP's extension directory, and write the ini activation file. No Zephir required — the C source is pre-generated in the package.
Clone the repository and run the bundled installer script. PHP dev headers and gcc must be present; Zephir is not required.
git clone https://github.com/php-io-extensions/fd
cd fd
bash install.shThe script builds from the pre-generated C source in ext/, installs the .so, and enables the extension across all detected SAPIs (CLI, FPM, Apache).
php -m | grep fd
php --ri fdAll methods are static on the Fd\FD class.
$flags parameters accept plain integers. They map directly to the POSIX constants from fcntl.h — PHP does not define these, so pass the values directly or define your own:
| Constant | Linux | macOS |
|---|---|---|
O_RDONLY |
0 |
0 |
O_WRONLY |
1 |
1 |
O_RDWR |
2 |
2 |
O_NONBLOCK |
2048 |
4 |
Combine multiple flags with the bitwise OR operator (|):
// O_WRONLY | O_NONBLOCK on Linux
$fd = Fd\FD::open('/dev/ttyUSB0', 1 | 2048);Opens a file or device and returns a file descriptor integer.
$fd = Fd\FD::open(string $device_path, int $flags): intReturns the file descriptor on success, or a negative value on failure.
// Open an I2C device for reading and writing (O_RDWR = 2)
$fd = Fd\FD::open('/dev/i2c-1', 2);
if ($fd < 0) {
throw new RuntimeException("Failed to open device");
}Closes an open file descriptor.
$result = Fd\FD::close(int $fd): intReturns 0 on success, -1 on failure.
Fd\FD::close($fd);Adds file status flags to an open file descriptor using fcntl. Existing flags are preserved; the provided flags are OR'd in.
$result = Fd\FD::addFlags(int $fd, int $flags): intReturns 0 on success, -1 on failure. Useful for setting a descriptor non-blocking after it has already been opened.
// Set O_NONBLOCK on an open descriptor (Linux: 2048, macOS: 4)
Fd\FD::addFlags($fd, 2048);
// Combine flags: O_NONBLOCK | O_APPEND on Linux
Fd\FD::addFlags($fd, 2048 | 1024);Reads up to $bytes_to_read bytes from a file descriptor and returns them as a string.
$data = Fd\FD::read(int $fd, int $bytes_to_read): stringReturns the bytes read as a binary string. Returns an empty string on error. A zero-length read (e.g. EOF) also returns an empty string, so callers that need to distinguish the two cases should check the descriptor state beforehand.
// Read 2 bytes from a UART device
$bytes = Fd\FD::read($fd, 2);Writes bytes to a file descriptor.
$written = Fd\FD::write(int $fd, string $data, int $bytes_to_write): intReturns the number of bytes written on success, or -1 on failure.
// Send a command byte over SPI
Fd\FD::write($fd, "\x3A", 1);// O_RDWR = 2
$fd = Fd\FD::open('/dev/i2c-1', 2);
if ($fd < 0) {
throw new RuntimeException('Could not open I2C device');
}
// Write a register address
Fd\FD::write($fd, "\x00", 1);
// Read 2 bytes back
$data = Fd\FD::read($fd, 2);
Fd\FD::close($fd);Project Saturn Studios, LLC.