PHP TRUE ASYNC is an experimental PHP extension that provides a true asynchronous,
tightly integrated at the core level.
Coming soon!
PHP TRUE ASYNC is supported for PHP 8.5.0 and later.
LibUV is the primary reactor implementation for this extension.
# Build the image
docker build -t true-async-php .
# Run interactively
docker run -it true-async-php bash
# Check TrueAsync module
docker run --rm true-async-php php -m | grep true_async- PHP 8.5.0+
- LibUV β₯ 1.45.0 (required) - Fixes critical
UV_RUN_ONCEbusy loop issue that could cause high CPU usage
Prior to libuv 1.44, there was a critical issue in uv__io_poll()/uv__run_pending logic that could cause the event loop to "stick" after the first callback when running in UV_RUN_ONCE mode, especially when new ready events appeared within callbacks. This resulted in:
- High CPU usage due to busy loops
- Performance degradation in async applications
- Inconsistent event loop behavior affecting TrueAsync API reliability
The fix in libuv 1.44 ensures that UV_RUN_ONCE properly returns after processing all ready callbacks in the current iteration, meeting the "forward progress" specification requirements. This is essential for TrueAsync's performance and reliability.
-
Clone the PHP repository:
for example, basic directory name is
php-src:git clone https://github.com/true-async/php-src -b true-async-api ./php-src -
Clone the
True Asyncextension repository:to the
extdirectory of your PHP source:git clone https://github.com/true-async/php-async ./php-src/ext/async -
Install PHP development tools:
Make sure you have the necessary development tools installed. On Debian/Ubuntu, you can run:
sudo apt-get install php-dev build-essential autoconf libtool pkg-configFor macOS, you can use Homebrew:
brew install autoconf automake libtool pkg-config -
Install LibUV::
IMPORTANT: LibUV version 1.45.0 or later is required.
For Debian/Ubuntu:
# Check if system libuv meets requirements (β₯1.45.0)
pkg-config --modversion libuv
# If version is too old, install from source:
wget https://github.com/libuv/libuv/archive/v1.45.0.tar.gz
tar -xzf v1.45.0.tar.gz
cd libuv-1.45.0
mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
make -j$(nproc)
sudo make install
sudo ldconfigFor macOS:
# Homebrew usually has recent versions
brew install libuvPlease see the LibUV installation guide for more details.
-
Configure and build:
./buildconf ./configure --enable-async make && sudo make installWe can use
--enable-debugto enable debug mode, which is useful for development.Note: The
--enable-experimental-async-apioption is no longer needed as the Async API is now enabled by default in the core.
-
Install php-sdk:
Download and set up php-sdk for building PHP extensions on Windows. -
Install and build LibUV:
You can use vcpkg or build libuv from source. -
Copy LibUV files to PHP SDK directories:
1. Copy everything from 'libuv\include' to '%PHP_SDK_PATH%\deps\include\libuv\' 2. Copy 'libuv.lib' to '%PHP_SDK_PATH%\deps\lib\'%PHP_SDK_PATH%is your php-sdk installation root. -
Configure and build the extension with PHP:
cd \path\to\php-src buildconf configure --enable-async nmakeNote: The
--enable-experimental-async-apioption is no longer needed as the Async API is now enabled by default in the core.
50+ PHP functions have been adapted to work asynchronously when used within coroutines:
gethostbyname()- resolve hostname to IP addressgethostbyaddr()- resolve IP address to hostnamegethostbynamel()- get list of IP addresses for hostname
- PDO MySQL - async-compatible PDO operations
PDO::__construct(),PDO::prepare(),PDO::exec()- non-blockingPDOStatement::execute(),PDOStatement::fetch()- async data access
- MySQLi - async-compatible MySQLi operations
mysqli_connect(),mysqli_query(),mysqli_prepare()- non-blockingmysqli_stmt_execute(),mysqli_fetch_*()- async result fetching
curl_exec()- execute cURL requestcurl_multi_exec()- execute multiple cURL handlescurl_multi_select()- wait for activity on multiple cURL handlescurl_multi_getcontent()- get content from multi handlecurl_setopt(),curl_getinfo(),curl_error(),curl_close()- async-aware
socket_connect(),socket_accept()- connection operationssocket_read(),socket_write()- data transfersocket_send(),socket_recv()- data exchangesocket_sendto(),socket_recvfrom()- addressed data transfersocket_bind(),socket_listen()- server operationssocket_select()- monitor socket activity
file_get_contents()- get file/URL contentsfread(),fwrite()- file I/O operationsfopen(),fclose()- file handle managementstream_socket_client(),stream_socket_server()- socket streamsstream_socket_accept()- accept stream connectionsstream_select()- monitor stream activitystream_context_create()- async-aware context creation
proc_open()- open process with pipesexec()- execute external commandshell_exec()- execute shell commandsystem()- execute system commandpassthru()- execute and pass output directly
sleep()- delay execution (seconds)usleep()- delay execution (microseconds)time_nanosleep()- nanosecond precision delaytime_sleep_until()- sleep until timestamp
ob_start()- start output buffering with coroutine isolationob_flush(),ob_clean()- buffer operations with isolationob_get_contents(),ob_end_clean()- get/end buffer with isolation
All functions automatically become non-blocking when used in async context, allowing other coroutines to continue execution while waiting for I/O operations to complete.
<?php
// Spawn multiple concurrent coroutines
Async\spawn(function() {
echo "Starting coroutine 1\n";
sleep(2); // Non-blocking in async context
echo "Coroutine 1 completed\n";
});
Async\spawn(function() {
echo "Starting coroutine 2\n";
sleep(1); // Non-blocking in async context
echo "Coroutine 2 completed\n";
});
echo "All coroutines started\n";<?php
$start = microtime(true);
// Start multiple DNS lookups concurrently
Async\spawn(function() {
$ip = gethostbyname('github.com'); // Non-blocking
$ips = gethostbynamel('github.com'); // Get all IPs
echo "GitHub: $ip (" . count($ips) . " total IPs)\n";
});
Async\spawn(function() {
$ip = gethostbyname('google.com'); // Non-blocking
$hostname = gethostbyaddr($ip); // Reverse lookup
echo "Google: $ip -> $hostname\n";
});
Async\spawn(function() {
$content = file_get_contents('http://httpbin.org/ip'); // Non-blocking
echo "External IP: " . json_decode($content)->origin . "\n";
});
$elapsed = microtime(true) - $start;
echo "All operations completed in: " . round($elapsed, 3) . "s\n";<?php
$urls = [
'https://httpbin.org/delay/1',
'https://httpbin.org/delay/2',
'https://httpbin.org/delay/1'
];
$start = microtime(true);
foreach ($urls as $i => $url) {
Async\spawn(function() use ($url, $i) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch); // Non-blocking
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
echo "Request $i: HTTP $httpCode\n";
});
}
$elapsed = microtime(true) - $start;
echo "All requests completed in: " . round($elapsed, 3) . "s\n";<?php
// Concurrent database queries with PDO MySQL
Async\spawn(function() {
$pdo = new PDO('mysql:host=localhost;dbname=test', $user, $pass);
// All operations are non-blocking in async context
$stmt = $pdo->prepare("SELECT * FROM users WHERE active = ?");
$stmt->execute([1]);
while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
echo "User: {$row['name']}\n";
}
});
// MySQLi concurrent operations
Async\spawn(function() {
$mysqli = new mysqli('localhost', $user, $pass, 'test');
// Non-blocking query execution
$result = $mysqli->query("SELECT COUNT(*) as total FROM orders");
$row = $result->fetch_assoc();
echo "Total orders: {$row['total']}\n";
$mysqli->close();
});
echo "Database queries started\n";<?php
// Execute multiple commands concurrently
Async\spawn(function() {
$output = shell_exec('sleep 2 && echo "Command 1 done"'); // Non-blocking
echo $output;
});
Async\spawn(function() {
$output = shell_exec('sleep 1 && echo "Command 2 done"'); // Non-blocking
echo $output;
});
echo "Commands started\n";<?php
// Each coroutine has isolated output buffer
Async\spawn(function() {
ob_start(); // Isolated buffer
echo "Output from coroutine 1\n";
echo "More output from coroutine 1\n";
$buffer1 = ob_get_contents();
ob_end_clean();
echo "Coroutine 1 captured: $buffer1";
});
Async\spawn(function() {
ob_start(); // Separate isolated buffer
echo "Output from coroutine 2\n";
$buffer2 = ob_get_contents();
ob_end_clean();
echo "Coroutine 2 captured: $buffer2";
});
echo "Buffers are isolated between coroutines\n";Pull requests and suggestions are welcome!
Please read CONTRIBUTING.md before starting.
- π οΈ php-src/true-async-api
- π php-async
- π php-true-async-rfc
PHP TRUE ASYNC β modern async PHP, today!