Native and transparent PHP coroutines and async/await without Swoole
Green threads for PHP, thanks to PHP 8.1 Fibers.
composer require moebius/coroutine
Final final update: 1.0.0-beta released
The API is stable, but there might still be bugs. It contains a small set of functions which can be used anywhere in your code base, and it SHOULD (but may - please report) not cause any side effects in existing code bases.
All these functions can be used anywhere in your application. When used, future coroutines will benefit.
M\go(callable): Coroutine launches a coroutine
M\await(Coroutine): mixed returns the value from the coroutine.
M\sleep(float): void pauses the coroutine and allows other coroutines to work.
M\unblock(resource): resource enables transparent context switching on blocking stream operations.
Final update: 0.0.5-alpha out I don’t think Reddit is the place to make announcements about this library, so I will make this final update to this post.
Automatic context switching between coroutines is supported for streams using protocol wrappers (file:// is currently the only one implemented)
http:// is probably very similar to file:// and I suspect implementing this one will make many other PHP functions asynchronous as well.
Some regression tests have been added to help avoid breaking things.
A small bug was fixed where a coroutine would not be allowed to complete when the process terminated.
UPDATE
Install it via composer:
```
composer require moebius/moebius
to try it out.
`` and try it out. Remember it requires PHP 8.1. Never tested it on Windows.
``` <?php use M{go, await, sleep}}; // these functions should get you started
function typewrite(string $message) { for ($i = 0; $i < mb_strlen($message); $i++) { echo mb_substr($message, $i, 1); sleep(0.3); } }
go(typewrite(...), "H world!\n"); echo "ello"; ```
/UPDATE
PS: This is pure PHP 8.1 without any PECL modules...
I've been working on a small micro-framework to make it easier to work with asynchronous and parallel code, and I would love some feedback and perhaps somebody would like to get involved with this "experiment" - it is already proving useful to me.
Most importantly: I'm designing this to work with existing code. It is quite different from ReactPHP and Amphp.
Features so far:
M::go()
for launchingFiber
based coroutines (which return a Promise object).M::await()
for synchronously awaiting promises (very useful with thego()
function).- "Invisible event loop" using
register_shutdown_function()
,
Work in progress:
- Support for non-blocking IO using
stream_select()
. This is almost complete. - "Magically" making old blocking code asynchronous. My implementation seems to be working, but I'm not ready to reveal it yet.
Wishlist:
- TCP server implementation which launches coroutines to handle connections.
- HTTP server implementation that builds on top of the TCP server implementation.
- Non-blocking database drivers (preferably compatible with PDO).
- Resource pool implementation (to manage multiple concurrent database connections for example).
I will try to publish this later tonight. Working on documentation so that people can start trying it at once.
Resolving a promise:
<?php
function my_existing_synchronous_function() {
// Any promise (with a `then(callable, callable)` method)
$result = M::await($somePromise);
}
Run something in parallel: ``` <?php // 5 seconds coroutine $workFor5Seconds = M::go(function() { for ($i = 0; $i < 5; $i++) { M::sleep(1); } });
// 2 seconds coroutine $workFor2Seconds = M::go(function() { for ($i = 0; $i < 2; $i++) { M::sleep(1); } });
// Busy coroutine with file IO $workForAWhile = M::go(function() { file_put_contents('/tmp/some-file.txt', 'Hello'); for ($i = 0; $i < 10000; $i++) { // Reading from the file system in a blocking manner // will allow other coroutines to make some progress. file_get_contents('/tmp/some-file.txt'); } unlink('/tmp/some-file.txt'); });
// Now wait for both co-routines to finish await($workFor2Seconds, $workFor5Seconds, $workForAWhile); echo "This took 5 seconds\n"; ```
I think this is surprisingly awesome, which is why I am posting one of my first posts here on reddit...