[VOTE] True Async RFC 1.6

php.internals

Edmond Dantes

286 days ago
Hello all According to all previous discussions, version 1.6 of this RFC has been prepared and is now being submitted for a vote: Voting Page: https://wiki.php.net/rfc/true_async/voting RFC https://wiki.php.net/rfc/true_async The vote officially starts tomorrow, as previously announced. For version 1.6 the following important change was made: All input/output functions are now bound by the shared requirement of being non-blocking with respect to the process. However, the specific behavior of each function may (optionally) be defined in separate RFCs. Thus, I/O functions themselves are not part of this RFC, but the main RFC defines the general way in which they must operate. Thus (as I see it), the RFC achieves a balance between cohesion and separation of concerns. Since the discussion period has ended, I will not be engaging in further debate (except regarding the voting process itself). If you have any questions for me of any kind, you may ask them either in a separate thread or privately. (This also means that I will not be answering RFC-related questions in this thread). I will be glad to hear your opinions and feedback. I wish all participants the best of luck. --- Best Regards, Ed

Derick Rethans

286 days ago
On 19 November 2025 12:35:13 GMT, Edmond Dantes <edmond.ht@gmail.com> wrote:
>Hello all > >According to all previous discussions, version 1.6 of this RFC has >been prepared and is now being submitted for a vote: > >Voting Page: https://wiki.php.net/rfc/true_async/voting >RFC https://wiki.php.net/rfc/true_async > >The vote officially starts tomorrow, as previously announced.
Why is the voting widget not on the RFC page where it belongs? cheers Derick

Matteo Beccati

286 days ago
Hi, Il 19/11/2025 14:24, Derick Rethans ha scritto:
> On 19 November 2025 12:35:13 GMT, Edmond Dantes <edmond.ht@gmail.com> wrote: >> Hello all >> >> According to all previous discussions, version 1.6 of this RFC has >> been prepared and is now being submitted for a vote: >> >> Voting Page: https://wiki.php.net/rfc/true_async/voting >> RFC https://wiki.php.net/rfc/true_async >> >> The vote officially starts tomorrow, as previously announced. > > Why is the voting widget not on the RFC page where it belongs?
Also it's weird to get a [VOTE] email when voting is not open yet. Cheers
-- Matteo Beccati

Edmond Dantes

286 days ago
Hello, Matteo
> Why is the voting widget not on the RFC page where it belongs?
Unfortunately, I wasn’t able to understand exactly how the voting process works, so I created a separate page. But if the vote is supposed to be included in the RFC itself, please ignore it.
> Also it's weird to get a [VOTE] email when voting is not open yet.
I’m sorry, this really is strange.

Rowan Tommins [IMSoP]

285 days ago
On 19/11/2025 15:22, Edmond Dantes wrote:
> Unfortunately, I wasn’t able to understand exactly how the voting > process works, so I created a separate page. But if the vote is > supposed to be included in the RFC itself, please ignore it.
I guess you didn't see my reply when you asked this before. Here again is the link to the instructions: https://wiki.php.net/rfc/howto Can you tell us where you got the idea that there needed to be a separate page? Maybe there is some text that we can make clearer. Meanwhile, please cancel this vote. There is no rush. If we get it right, this could be the most significant feature added to PHP for 10 years or more. If we get it wrong, we might regret it for 20 years. On Monday, you replied to a message from Bart Vanhoutte, saying:
> Hello Bart. > I am ready to agree with every word. > > Participation from a working group, framework representatives, and the > ability to adapt libraries in advance would remove the concerns that > are currently causing fear. This is probably the only effective > process for developing and introducing such a feature.
Then two days later, you decided that no more discussion was necessary, and opened a vote. This feels like a complete contradiction. Let's find a way to get that working group set up, and get people from other projects involved. I am extremely grateful and frankly amazed for all the work you've put into this. Please don't throw that away by ending up with a "No" vote over small details.
-- Rowan Tommins [IMSoP]

Daniil Gentili

285 days ago
Hi,
>> Hello Bart. >> I am ready to agree with every word. >> >> Participation from a working group, framework representatives, and the >> ability to adapt libraries in advance would remove the concerns that >> are currently causing fear. This is probably the only effective >> process for developing and introducing such a feature. > > Then two days later, you decided that no more discussion was > necessary, and opened a vote. > > This feels like a complete contradiction. > > Let's find a way to get that working group set up, and get people from > other projects involved. >
My key takeaway from Bart's message is:
> Moreover, even though there are quite a few people in the community
who have the knowledge required because they either develop or work with aforementioned libraries or extensions, (almost) none of them seem to be involved in discussing this RFC.
> For an RFC that can drastically change the way we develop
applications I would expect more experts on this matter to be involved. Ideally, PHP core developers, library developers & maintainers, IDE developers, ..., would develop software using this branch to at least get some feel for the paradigm and this RFC in general. I absolutely agree with this take, however, so far, the discussion around this RFC has been, in my opinion, mostly bikeshedding, with theoretical correctness proposals that are an absolute nightmare in practice (like structured concurrency), proposed by people that admittedly have never written extensive amounts of async code in languages using multiple paradigms, and thus haven't: - Experienced the pain of writing async with colored functions - Experienced the footguns of structured concurrency - And on the other hand, haven't experienced the pleasure and simplicity of safely writing async code in languages like Go, or in PHP using AMPHP (which use uncolored and unstructured concurrency, the kind proposed and championed by edmond) While a working group *can* steer the conversation away from theoretically correct but practically unusable approaches, that can happen only if - The correct people (i.e. async library maintainers, or people that write async logic every day in multiple languages like myself) are present - They are given more weight than the average PHP developer who hasn't used async much if at all, and can only make theoretical proposals not based on practice and experience I'm afraid that given the current state of the PHP community, which is largely new to async, the quality of the conversation in a working group would not be much higher than the one I'm seeing in this RFC, and would just protract even longer the agony of design by committee, where in reality what's needed is a single, clear and correct vision (which Edmond has), without influences from unexperienced people making proposals based purely on abstract/theoretical PoVs. Regards, Daniil Gentili.

Rob Landers

285 days ago
On Thu, Nov 20, 2025, at 11:18, Daniil Gentili wrote:
> Hi, > > >> Hello Bart. > >> I am ready to agree with every word. > >> > >> Participation from a working group, framework representatives, and the > >> ability to adapt libraries in advance would remove the concerns that > >> are currently causing fear. This is probably the only effective > >> process for developing and introducing such a feature. > > > > Then two days later, you decided that no more discussion was > > necessary, and opened a vote. > > > > This feels like a complete contradiction. > > > > Let's find a way to get that working group set up, and get people from > > other projects involved. > > > > My key takeaway from Bart's message is: > > > Moreover, even though there are quite a few people in the community > who have the knowledge required because they either develop or work with > aforementioned libraries or extensions, (almost) none of them seem to be > involved in discussing this RFC. > > For an RFC that can drastically change the way we develop > applications I would expect more experts on this matter to be involved. > Ideally, PHP core developers, library developers & maintainers, IDE > developers, ..., would develop software using this branch to at least > get some feel for the paradigm and this RFC in general. > > I absolutely agree with this take, however, so far, the discussion > around this RFC has been, in my opinion, mostly bikeshedding, with > theoretical correctness proposals that are an absolute nightmare in > practice (like structured concurrency), proposed by people that > admittedly have never written extensive amounts of async code in > languages using multiple paradigms, and thus haven't: > > - Experienced the pain of writing async with colored functions > - Experienced the footguns of structured concurrency > - And on the other hand, haven't experienced the pleasure and simplicity > of safely writing async code in languages like Go, or in PHP using AMPHP > (which use uncolored and unstructured concurrency, the kind proposed and > championed by edmond) > > While a working group *can* steer the conversation away from > theoretically correct but practically unusable approaches, that can > happen only if > > - The correct people (i.e. async library maintainers, or people that > write async logic every day in multiple languages like myself) are present > - They are given more weight than the average PHP developer who hasn't > used async much if at all, and can only make theoretical proposals not > based on practice and experience > > I'm afraid that given the current state of the PHP community, which is > largely new to async, the quality of the conversation in a working group > would not be much higher than the one I'm seeing in this RFC, and would > just protract even longer the agony of design by committee, where in > reality what's needed is a single, clear and correct vision (which > Edmond has), without influences from unexperienced people making > proposals based purely on abstract/theoretical PoVs. > > Regards, > > Daniil Gentili.
I kind of take offence to this statement. First of all, I work almost exclusively in Go these days, and on FrankenPHP. I only brought up colored functions twice, because literally every language that has attempted the proposed solution here have all reversed course and implemented coloured functions. I've hand-written schedulers in C# (which is also cooperatively scheduled). Second, Go has a tremendous amount of primitives for dealing with concurrency: wait groups, locks, atomics, etc. This proposal has none of that, hence my concern with suspension points. Further, I have worked with AMPHP extensively since it's generator-based days on multiple projects, and with Fibers quite extensively as well. So saying "proposed by people that admittedly have never written extensive amounts of async code in languages using multiple paradigms" is factually untrue. — Rob

Deleu

285 days ago
On Thu, 20 Nov 2025 at 07:21 Daniil Gentili <daniil.gentili@gmail.com> wrote:
> Hi, > > >> Hello Bart. > >> I am ready to agree with every word. > >> > >> Participation from a working group, framework representatives, and the > >> ability to adapt libraries in advance would remove the concerns that > >> are currently causing fear. This is probably the only effective > >> process for developing and introducing such a feature. > > > > Then two days later, you decided that no more discussion was > > necessary, and opened a vote. > > > > This feels like a complete contradiction. > > > > Let's find a way to get that working group set up, and get people from > > other projects involved. > > > > My key takeaway from Bart's message is: > > > Moreover, even though there are quite a few people in the community > who have the knowledge required because they either develop or work with > aforementioned libraries or extensions, (almost) none of them seem to be > involved in discussing this RFC. > > For an RFC that can drastically change the way we develop > applications I would expect more experts on this matter to be involved. > Ideally, PHP core developers, library developers & maintainers, IDE > developers, ..., would develop software using this branch to at least > get some feel for the paradigm and this RFC in general. > > I absolutely agree with this take, however, so far, the discussion > around this RFC has been, in my opinion, mostly bikeshedding, with > theoretical correctness proposals that are an absolute nightmare in > practice (like structured concurrency), proposed by people that > admittedly have never written extensive amounts of async code in > languages using multiple paradigms, and thus haven't: > > - Experienced the pain of writing async with colored functions > - Experienced the footguns of structured concurrency > - And on the other hand, haven't experienced the pleasure and simplicity > of safely writing async code in languages like Go, or in PHP using AMPHP > (which use uncolored and unstructured concurrency, the kind proposed and > championed by edmond) > > While a working group *can* steer the conversation away from > theoretically correct but practically unusable approaches, that can > happen only if > > - The correct people (i.e. async library maintainers, or people that > write async logic every day in multiple languages like myself) are present > - They are given more weight than the average PHP developer who hasn't > used async much if at all, and can only make theoretical proposals not > based on practice and experience > > I'm afraid that given the current state of the PHP community, which is > largely new to async, the quality of the conversation in a working group > would not be much higher than the one I'm seeing in this RFC, and would > just protract even longer the agony of design by committee, where in > reality what's needed is a single, clear and correct vision (which > Edmond has), without influences from unexperienced people making > proposals based purely on abstract/theoretical PoVs. > > Regards, > > Daniil Gentili.
While I certainly can sympathize with the painful, dreadful, unpleasant, unbearable agony of debating a subject with “non-experts”, it’s important to have some perspective in the opposite direction. As it has been mentioned before, Async PHP in general is practically a rounding error in terms of user base and there are reasons for that. It’s important to remember that the benefits of async doesn’t always justify the burden that it brings. For PHP as a language to adopt an async solution natively it’s very important that sync code continues to function while also allowing developers to opt into async without having to feel like they changed languages and must re-learn how to manage their projects. If this is not possible then perhaps the current state is as good as we can ever get: let expert matter install their extension (opt-in) on a per-project basis. It's going to be up to the subject experts to come up with a path that allows PHP to stay coherent while offering both approaches. To put this in another way: RFC Voters are above average PHP developers. If they're unable to digest the changes being proposed, even if said changes are being proposed by the single most subject-expert human on the planet, then how do we expect average PHP developers to make good use of it?

Jakub Zelenka

285 days ago
On Thu, Nov 20, 2025 at 1:04 PM Deleu <deleugyn@gmail.com> wrote:
> On Thu, 20 Nov 2025 at 07:21 Daniil Gentili <daniil.gentili@gmail.com> > wrote: > >> Hi, >> >> >> Hello Bart. >> >> I am ready to agree with every word. >> >> >> >> Participation from a working group, framework representatives, and the >> >> ability to adapt libraries in advance would remove the concerns that >> >> are currently causing fear. This is probably the only effective >> >> process for developing and introducing such a feature. >> > >> > Then two days later, you decided that no more discussion was >> > necessary, and opened a vote. >> > >> > This feels like a complete contradiction. >> > >> > Let's find a way to get that working group set up, and get people from >> > other projects involved. >> > >> >> My key takeaway from Bart's message is: >> >> > Moreover, even though there are quite a few people in the community >> who have the knowledge required because they either develop or work with >> aforementioned libraries or extensions, (almost) none of them seem to be >> involved in discussing this RFC. >> > For an RFC that can drastically change the way we develop >> applications I would expect more experts on this matter to be involved. >> Ideally, PHP core developers, library developers & maintainers, IDE >> developers, ..., would develop software using this branch to at least >> get some feel for the paradigm and this RFC in general. >> >> I absolutely agree with this take, however, so far, the discussion >> around this RFC has been, in my opinion, mostly bikeshedding, with >> theoretical correctness proposals that are an absolute nightmare in >> practice (like structured concurrency), proposed by people that >> admittedly have never written extensive amounts of async code in >> languages using multiple paradigms, and thus haven't: >> >> - Experienced the pain of writing async with colored functions >> - Experienced the footguns of structured concurrency >> - And on the other hand, haven't experienced the pleasure and simplicity >> of safely writing async code in languages like Go, or in PHP using AMPHP >> (which use uncolored and unstructured concurrency, the kind proposed and >> championed by edmond) >> >> While a working group *can* steer the conversation away from >> theoretically correct but practically unusable approaches, that can >> happen only if >> >> - The correct people (i.e. async library maintainers, or people that >> write async logic every day in multiple languages like myself) are present >> - They are given more weight than the average PHP developer who hasn't >> used async much if at all, and can only make theoretical proposals not >> based on practice and experience >> >> I'm afraid that given the current state of the PHP community, which is >> largely new to async, the quality of the conversation in a working group >> would not be much higher than the one I'm seeing in this RFC, and would >> just protract even longer the agony of design by committee, where in >> reality what's needed is a single, clear and correct vision (which >> Edmond has), without influences from unexperienced people making >> proposals based purely on abstract/theoretical PoVs. >> >> Regards, >> >> Daniil Gentili. > > > While I certainly can sympathize with the painful, dreadful, unpleasant, > unbearable agony of debating a subject with “non-experts”, it’s important > to have some perspective in the opposite direction. > > As it has been mentioned before, Async PHP in general is practically a > rounding error in terms of user base and there are reasons for that. It’s > important to remember that the benefits of async doesn’t always justify the > burden that it brings. For PHP as a language to adopt an async solution > natively it’s very important that sync code continues to function while > also allowing developers to opt into async without having to feel like they > changed languages and must re-learn how to manage their projects. If this > is not possible then perhaps the current state is as good as we can ever > get: let expert matter install their extension (opt-in) on a per-project > basis. > > It's going to be up to the subject experts to come up with a path that > allows PHP to stay coherent while offering both approaches. To put this in > another way: RFC Voters are above average PHP developers. If they're unable > to digest the changes being proposed, even if said changes are being > proposed by the single most subject-expert human on the planet, then how do > we expect average PHP developers to make good use of it? >
Yeah I think this is one of the reasons why the RFC failed. It couldn't properly explain the topic even though it was reduced to minimum. One of the factor is certainly that Edmond is new to the RFC process but more importantly it's quite contentious topic that can bring even more bike shedding. I think there were some important points raised in the discussions about safety of the existing sync code which I think might be the real killer here. So even if we omit the mix up with the pre-announcement and sudden voting (that were sure path to rejection), I think the bigger problem is the whole size of the feature and the fact that it will be extremely hard to find any solution that will please majority of voters. I'm honestly not sure if this is possible to get to any form that can pass. I would like to be wrong but we can see the reality here. I think the way forward for PHP is to do what we have been doing and it is to provide the building blocks for user space to enable async there because that's something that can be reasonably introduced using the RFC through smaller chunks. It means improving the non blocking setup, exposing IO hooks, better polling and other primitives. That was actually the plan in past and that's why it is also contained in my STF stream work where the scope was created way before the TrueAsync. Kind regards, Jakub

Luís Vinícius Santos da Costa Barros

285 days ago
Hi What I understand from this is that the voters are not willing to make any concessions to include a feature that is important to at least 10% of the developers in the PHP ecosystem (someone mentioned 10% in an earlier email; I would guess the number is even higher, considering China). It’s important for us, who are part of that 10% that apparently isn’t important enough to be heard, to know what the voters’ stance actually is regarding the introduction of tooling for Async in PHP. That way we can understand what the next step needs to be: whether we take on the difficult job of migrating our software from PHP to another platform, or the impossible job of trying to convince people who are not willing to be convinced. Regards, Luís Vinícius Em qui., 20 de nov. de 2025, 12:44, Jakub Zelenka <bukka@php.net> escreveu:

Edmond Dantes

285 days ago
Hello
> I'm afraid that given the current state of the PHP community, which is > largely new to async, the quality of the conversation in a working group > would not be much higher than the one I'm seeing in this RFC
I’m afraid I have to agree with every word said here, even though we don’t know each other and have never spoken before. And this post is the most honest explanation of the current situation. --- Ed

Rowan Tommins [IMSoP]

284 days ago
On 20/11/2025 10:18, Daniil Gentili wrote:
> I absolutely agree with this take, however, so far, the discussion > around this RFC has been, in my opinion, mostly bikeshedding, with > theoretical correctness proposals that are an absolute nightmare in > practice (like structured concurrency), proposed by people that > admittedly have never written extensive amounts of async code in > languages using multiple paradigms
That is exactly why I think some other process is needed, and why an RFC vote right now doesn't tell us anything useful. I really want this feature. But I really want us to get it right. As someone who is completely new to the topic, the things that will make me vote "Yes" on an async proposal, are: 1) Confidence that experts have reviewed the design, and ironed out crucial details. From what I've seen, Edmond has done an extremely thorough job. Once again, thank you, I tip my hat to your effort and dedication. But I'm not qualified to judge the result, and I want to see opinions from people who are. It may be that after discussing it with a bunch of other experts, exactly the same technical design would come out; I just want a bit of reassurance that the right discussions have happened. Has there been an attempt to invite specific people into a discussion, e.g. people who've worked on ReactPHP, Swoole, etc? 2) A cast-iron promise that existing code would run unchanged; or a clear explanation of what modifications it would need. The "Goals" section says code will not need changes "or changes should be minimal"; and will run without modification "provided that..." Those caveats worry me. I work on a code base with a million lines of PHP code developed in half a dozen frameworks over twenty years, maintained by a small team. When I hear "it took us 2-3 days to update a library", I don't hear a small number; I multiply it in my head, and wonder how I would justify weeks of development and testing. Right now, I don't even understand what we would be spending those weeks doing. If it really is a case of "it will be fine unless you're doing some weird tricks to force PHP to do things it doesn't normally do", then great! If it ends up as "set this global setting, and you won't get the benefit, but your legacy code will run fine", I can live with that. If the only advice is "your code will be fine as long it's already well-architected and completely covered by automated testing", it's no use to me - that's just not the reality I live in. I sincerely hope that this is solvable. 3) A set of "idiot-proof" high-level features that a non-expert PHP developer can use without understanding the full implementation. The current RFC is a lot more focused than previous versions, and that is great; but it still gets very quickly into the mechanics and edge-cases that require expertise to use. What I think is missing is the pitch to users. Something that we can put on the php.net homepage, and say "look how easy concurrency is in PHP 9!" To paraphrase the Perl slogan, I want the language to make the common things easy, and the complex things possible. I want a "pit of success", where the easiest thing to write is also the most likely to be safe and useful. I think that mostly just means defining some syntax sugar on top of some of the functions and objects. Something that we can say is PHP's equivalent of async/await, or goroutines and channels. But ... I realise this can't all fit into one RFC without discussion going on forever. Which is why we need to find some new process to make it possible. On 20/11/2025 15:39, Jakub Zelenka wrote:
> I think the way forward for PHP is to do what we have been doing and > it is to provide the building blocks for user space to enable async > there because that's something that can be reasonably introduced using > the RFC through smaller chunks.
I think it would be a shame to throw away the progress that's been made. I wonder if a way forward could be something like this: 1) Agree a Project Charter, some Goals and Non-Goals. Hold a vote on this, to agree that we're going ahead with the project 2) Set up some kind of project tracker, where we can list open questions and design tasks; maybe a separate mailing list / forum / chatroom for those involved in the details 3) Start with Edmond's amazing work, and iteratively work on those individual questions 4) Keep bikeshedding questions (e.g. "what is the best name for this class, and should it extend Error or Exception?") separate from architecture questions (e.g. "is this class necessary, or should it be hidden from the user?") 5) Converge on a solution where we've already agreed everything in bite-sized pieces 6) Release PHP 9.0 and celebrate All of that requires that enough people who actually understand the problem space deeply are willing to be involved and collaborate. I really hope that's the case, but I am not in a position to volunteer myself.
-- Rowan Tommins [IMSoP]

Edmond Dantes

284 days ago
Hello. Imagine that we have an application like this. ```php class AuthService { private static ?self $instance = null; private PDO $db; private ?string $sessionId = null; // Private constructor for singleton private function __construct(PDO $db) { $this->db = $db; } // Get singleton instance public static function getInstance(PDO $db): self { if (self::$instance === null) { self::$instance = new self($db); } return self::$instance; } public function login(string $email, string $password): bool { // Find user by email $stmt = $this->db->prepare('SELECT * FROM users WHERE email = ?'); $stmt->execute([$email]); $user = $stmt->fetch(PDO::FETCH_ASSOC); // Invalid credentials if (!$user || !password_verify($password, $user['password_hash'])) { return false; } // Generate and save session ID $this->sessionId = bin2hex(random_bytes(16)); $stmt = $this->db->prepare( 'INSERT INTO sessions (user_id, session_id) VALUES (?, ?)' ); $stmt->execute([$user['id'], $this->sessionId]); return true; } // Return current session ID public function getSessionId(): ?string { return $this->sessionId; } } ``` One day you decide you want more performance and make a single PHP process handle multiple connections concurrently. You wrap each request in a separate coroutine and try to use the old code. ```php $server = new Swoole\Http\Server("127.0.0.1", 9501); $server->on("request", function ($req, $res) { // create DB connection (just for example) $db = new PDO('mysql:host=localhost;dbname=test', 'root', ''); // get singleton $auth = AuthService::getInstance($db); // read request data $data = json_decode($req->rawContent(), true); $email = $data['email'] ?? ''; $password = $data['password'] ?? ''; // call old sync code $ok = $auth->login($email, $password); if ($ok) { $res->end("Logged in, session: " . $auth->getSessionId()); } else { $res->status(401); $res->end("Invalid credentials"); } }); $server->start(); ``` What is happening here? Now, in PHP, inside a single process or thread, the same code is literally handling multiple connections. At the same time, there are constant switches between different requests at the points where MySQL queries occur. That is, when the code executes $stmt->execute([$email]); control is passed to another coroutine with a different $stmt->execute([$email]); What breaks in this code? Correct, coroutines break the singleton because they alternate writing different Session IDs! And what does not change in this code? The SQL queries can remain unchanged. The first problem with shared memory between coroutines can ONLY be solved by the programmer. Only the programmer. There is no solution that would make this happen automatically. Yesterday we talked about how we can help the programmer detect such situations during debugging. But in any case, only the programmer **CAN** and **MUST** solve this problem. The difference is that you don’t need to rewrite everything else. The focus is only on the issue of concurrent access to memory. The essence of the choice is how much code needs to be rewritten. Almost everything, or only the code with global state. My choice is: it’s better to rewrite only the code with global state — or switch to Go and avoid the pain :) As for the rest, I will write a separate message so as not to clutter things up. ---- Edmond

=?utf-8?B?6Z+p5aSp5bOw?=

284 days ago
My thanks to Edmond for his work on PHP async I/O. Regardless of how the vote turns out, I believe everything is moving in a positive direction. This will undoubtedly leave a bold mark in the history of PHP’s evolution. I’ll share some information and thoughts to help everyone understand async. These may include views on PHP Fiber, amphp, reactphp, and FrankenPHP, but please remember they are purely technical reflections, with no praise or criticism implied. 1. What lies at the core of Swoole’s async design Using Boost.Context assembly to implement C/C++ stackful coroutines is no longer esoteric; PHP Fiber and Swoole are almost identical in their low-level principles. The only difference is that Swoole suspends and resumes coroutines entirely in C/C++, whereas PHP Fiber does the opposite—suspension happens in PHP code. While PHP also exposes relevant APIs, they are rarely used in Swoole. Because both the C stack and the PHP stack are fully preserved, this approach is actually very safe and won’t cause memory errors—unless static or global memory is misused. Swoole runs over 1,700 tests on GitHub Actions, many of which involve multiple coroutines issuing concurrent requests. Before testing, containers spin up mysql, pgsql, oracle, redis, firebirdsql, httpbin, tinyproxy, pure-ftpd, and many other databases and servers to interact with code in phpt files. The breadth of these tests speaks to its reliability. Unlike amphp/reactphp, Swoole does not invent new APIs; it reuses PHP’s existing functions. Swoole hooks into PHP streams, the standard library, and other extension functions—such as sleep, stream_socket_client, stream_socket_server, file_get_contents, fsockopen, curl_*, mysqli, pdo_mysql. Inside a Swoole coroutine, these calls are no longer synchronous blocking I/O; they become non-blocking. When I/O isn’t ready, the runtime suspends the current coroutine and uses epoll to watch for readable events, resuming the coroutine only when the operation completes. An example: Co\run(function() { &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sleep(1); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; echo fread($fp, 8192), PHP_EOL; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $conn = stream_socket_accept($fp); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; $redis = new Redis(); &nbsp; &nbsp; &nbsp; &nbsp; $redis-&gt;connect('127.0.0.1', 6379); &nbsp; &nbsp; &nbsp; &nbsp; while(true) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $redis-&gt;subscribe(['test'], function ($instance, $channelName, $message) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; echo 'New redis message: '.$channelName, "==&gt;", $message, PHP_EOL; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; $redis = new Redis(); &nbsp; &nbsp; &nbsp; &nbsp; $redis-&gt;connect('127.0.0.1', 6379); &nbsp; &nbsp; &nbsp; &nbsp; $count = 0; &nbsp; &nbsp; &nbsp; &nbsp; while(true) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sleep(2); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $redis-&gt;publish('test','hello, world, count='.$count++); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); }); By conventional understanding, this code shouldn’t run: every function that performs network I/O would block the entire process. But in the Swoole environment, the program runs smoothly. We can even modify the code to increase the number of clients by several thousand, and it still runs stably. Co\run(function() { &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sleep(1); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; echo fread($fp, 8192), PHP_EOL; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); &nbsp; &nbsp; $n = 2000; &nbsp; &nbsp; while($n--) { &nbsp; &nbsp; &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $conn = stream_socket_accept($fp); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; } }); Swoole’s aim is to leverage PHP’s existing ecosystem rather than build a new one. If we were starting from scratch—discarding PHP’s commonly used functions and learning an entirely new async API—why wouldn’t developers simply switch languages? Now that true-async has adopted Swoole’s approach, I think that’s an excellent choice. 2. Where PHP-FPM falls short If all you do is read/write MySQL and generate HTML, PHP-FPM is already superb. If I’m building a web project that only depends on MySQL, I wouldn’t use Swoole; PHP-FPM is the best choice. But many modern web projects need to call external HTTP APIs, and slow requests often render PHP-FPM unavailable, which is frustrating. Async exists precisely to address this. With the rise of ChatGPT, streaming responses such as SSE and full-duplex communication via WebSocket will become increasingly common—technologies that PHP-FPM doesn’t support well. Many developers choose Node.js or Go instead. The influence of Swoole or amphp remains limited; only a small subset of developers opt to stay with PHP for async programming using these solutions. If PHP can adopt true-async or other AsyncIO solutions and provide support for async I/O at the language level, it would be tremendous news for PHP users. In essence, async I/O is a runtime matter—much like Node.js in relation to V8. New PHP syntax isn’t required; Swoole, for instance, adds no new syntax—just some functions and classes—just as fastcgi_finish_request and fpm_get_status are php-fpm–only functions. 3. FrankenPHP FrankenPHP is a wonderful project that uses Go to give PHP additional capabilities, with great room for exploration. In an RFC for a Polling API, author Jakub Zelenka—also a FrankenPHP maintainer—shared a technical idea: consider implementing a goroutine version of the TSRM thread isolation scheme. Each goroutine would have its own Zend VM environment—essentially a goroutine-based php-fpm. I believe this approach may pose significant challenges, especially regarding memory resources. Today, when running Symfony or Laravel under PHP-FPM with 100–200 worker processes, memory pressure is already heavy. If each process consumes tens to over a hundred megabytes, the group can easily use up to 20 GB. With goroutines, if you launch thousands or tens of thousands to handle requests concurrently, memory usage could become enormous. By contrast, coroutines are designed to be very lightweight: a suspended coroutine should retain only the call stack and a small amount of request/session-related memory, while other resources can be shared and reused across requests. This drastically reduces memory usage while still allowing a large number of simultaneous requests. When a request is slow, suspension incurs little cost. 4. Fiber If Fiber and coroutines coexist as execution units, I agree it can be confusing. But the current Fiber simply can’t be used in a Swoole-like runtime with extensive low-level switching. Although Fiber landed in PHP 8.1, Swoole cannot use any Fiber APIs. In addition, Fiber doesn’t fully switch all global memory state—for example OG(handlers), BG(serialize), BG(unserialize)—so it’s unclear whether issues exist there. 5. Golang’s abundance of synchronization primitives Go’s goroutine isn’t purely a coroutine; it’s a combination of thread and coroutine, which necessitates many locks, mutexes, semaphores, and atomics to resolve data races. PHP does not support multithreading. Whether it’s Fiber, Swoole, or any other coroutine implementation in PHP, execution is single-threaded: only one coroutine runs at a time, and until it yields, no other coroutine runs. Therefore, PHP coroutines are not a complex concept but a clear and straightforward one. If the true-async RFC vote doesn’t pass this time, I think we can split the work into several parts, aiming for each RFC to accomplish just one thing. I think the most important task now is to allow coroutine switching in low-level C code, not just in PHP code. Whether we call it coroutines or Fiber 2.0 is fine. On top of that, other work can be introduced via future RFCs to progressively strengthen the design. Lastly, I sincerely hope PHP keeps getting better. Thanks all for your contributions. Open discussion is always beneficial. ------ Tianfeng Han &nbsp; ------------------&nbsp;Original&nbsp;------------------ From: &nbsp;"Edmond&nbsp;Dantes"<edmond.ht@gmail.com&gt;; Date: &nbsp;Fri, Nov 21, 2025 03:17 PM To: &nbsp;"Rowan Tommins [IMSoP]"<imsop.php@rwec.co.uk&gt;; Cc: &nbsp;"php internals"<internals@lists.php.net&gt;; Subject: &nbsp;Re: [PHP-DEV] [VOTE] True Async RFC 1.6 &nbsp; Hello. Imagine that we have an application like this. ```php class AuthService { &nbsp;&nbsp;&nbsp; private static ?self $instance = null; &nbsp;&nbsp;&nbsp; private PDO $db; &nbsp;&nbsp;&nbsp; private ?string $sessionId = null; &nbsp;&nbsp;&nbsp; // Private constructor for singleton &nbsp;&nbsp;&nbsp; private function __construct(PDO $db) &nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;db = $db; &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp; // Get singleton instance &nbsp;&nbsp;&nbsp; public static function getInstance(PDO $db): self &nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (self::$instance === null) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; self::$instance = new self($db); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return self::$instance; &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp; public function login(string $email, string $password): bool &nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Find user by email &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $stmt = $this-&gt;db-&gt;prepare('SELECT * FROM users WHERE email = ?'); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $stmt-&gt;execute([$email]); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $user = $stmt-&gt;fetch(PDO::FETCH_ASSOC); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Invalid credentials &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!$user || !password_verify($password, $user['password_hash'])) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Generate and save session ID &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $this-&gt;sessionId = bin2hex(random_bytes(16)); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $stmt = $this-&gt;db-&gt;prepare( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 'INSERT INTO sessions (user_id, session_id) VALUES (?, ?)' &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $stmt-&gt;execute([$user['id'], $this-&gt;sessionId]); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return true; &nbsp;&nbsp;&nbsp; } &nbsp;&nbsp;&nbsp; // Return current session ID &nbsp;&nbsp;&nbsp; public function getSessionId(): ?string &nbsp;&nbsp;&nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return $this-&gt;sessionId; &nbsp;&nbsp;&nbsp; } } ``` One day you decide you want more performance and make a single PHP process handle multiple connections concurrently. You wrap each request in a separate coroutine and try to use the old code. ```php $server = new Swoole\Http\Server("127.0.0.1", 9501); $server-&gt;on("request", function ($req, $res) { &nbsp;&nbsp;&nbsp; // create DB connection (just for example) &nbsp;&nbsp;&nbsp; $db = new PDO('mysql:host=localhost;dbname=test', 'root', ''); &nbsp;&nbsp;&nbsp; // get singleton &nbsp;&nbsp;&nbsp; $auth = AuthService::getInstance($db); &nbsp;&nbsp;&nbsp; // read request data &nbsp;&nbsp;&nbsp; $data = json_decode($req-&gt;rawContent(), true); &nbsp;&nbsp;&nbsp; $email = $data['email'] ?? ''; &nbsp;&nbsp;&nbsp; $password = $data['password'] ?? ''; &nbsp;&nbsp;&nbsp; // call old sync code &nbsp;&nbsp;&nbsp; $ok = $auth-&gt;login($email, $password); &nbsp;&nbsp;&nbsp; if ($ok) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $res-&gt;end("Logged in, session: " . $auth-&gt;getSessionId()); &nbsp;&nbsp;&nbsp; } else { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $res-&gt;status(401); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $res-&gt;end("Invalid credentials"); &nbsp;&nbsp;&nbsp; } }); $server-&gt;start(); ``` What is happening here? Now, in PHP, inside a single process or thread, the same code is literally handling multiple connections. At the same time, there are constant switches between different requests at the points where MySQL queries occur. That is, when the code executes $stmt-&gt;execute([$email]); control is passed to another coroutine with a different $stmt-&gt;execute([$email]); What breaks in this code? Correct, coroutines break the singleton because they alternate writing different Session IDs! And what does not change in this code? The SQL queries can remain unchanged. The first problem with shared memory between coroutines can ONLY be solved by the programmer. Only the programmer. There is no solution that would make this happen automatically. Yesterday we talked about how we can help the programmer detect such situations during debugging. But in any case, only the programmer **CAN** and **MUST** solve this problem. The difference is that you don’t need to rewrite everything else. The focus is only on the issue of concurrent access to memory. The essence of the choice is how much code needs to be rewritten. Almost everything, or only the code with global state. My choice is: it’s better to rewrite only the code with global state — or switch to Go and avoid the pain :) As for the rest, I will write a separate message so as not to clutter things up. ---- Edmond

=?utf-8?B?6Z+p5aSp5bOw?=

284 days ago
To correct a mistake in previous email, there was an error in the second code snippet of my last email. The correct code is as follows: ```php <?php Co\run(function() { &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $conn = stream_socket_accept($fp); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Co\go(function() use ($conn) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sleep(1); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); &nbsp; &nbsp; $n = 2000; &nbsp; &nbsp; while($n--) { &nbsp; &nbsp; &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; echo fread($fp, 8192), PHP_EOL; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; } }); ``` The logic here is to start a server coroutine, which then accepts client connections. Each new connection spawns a coroutine that sends the current time string to the client every second. The code below directly creates 2,000 clients in the current process, connects them to the server, and reads data. Note: If you want to test this code, I recommend lowering the number of concurrent connections, otherwise your terminal may freeze or become unresponsive. This program can accommodate virtually any PHP function, such as mysqli, pdo, redis, curl, and more—all of which can be executed concurrently with ease. Notice that only two new APIs have been introduced in the code: `Co\run` and `Co\go`; the rest are standard or commonly used PHP extension functions. This is precisely the strength of Swoole, and the focus of the True-Async team’s ongoing work. Hopefully, the PHP language will one day include such powerful features natively. ---------- Tianfeng Han &nbsp; ------------------&nbsp;Original&nbsp;------------------ From: &nbsp;"韩天峰"<rango@swoole.com&gt;; Date: &nbsp;Fri, Nov 21, 2025 07:03 PM To: &nbsp;"Edmond Dantes"<edmond.ht@gmail.com&gt;; "Rowan Tommins [IMSoP]"<imsop.php@rwec.co.uk&gt;; Cc: &nbsp;"php internals"<internals@lists.php.net&gt;; Subject: &nbsp;Re: [PHP-DEV] [VOTE] True Async RFC 1.6 &nbsp; My thanks to Edmond for his work on PHP async I/O. Regardless of how the vote turns out, I believe everything is moving in a positive direction. This will undoubtedly leave a bold mark in the history of PHP’s evolution. I’ll share some information and thoughts to help everyone understand async. These may include views on PHP Fiber, amphp, reactphp, and FrankenPHP, but please remember they are purely technical reflections, with no praise or criticism implied. 1. What lies at the core of Swoole’s async design Using Boost.Context assembly to implement C/C++ stackful coroutines is no longer esoteric; PHP Fiber and Swoole are almost identical in their low-level principles. The only difference is that Swoole suspends and resumes coroutines entirely in C/C++, whereas PHP Fiber does the opposite—suspension happens in PHP code. While PHP also exposes relevant APIs, they are rarely used in Swoole. Because both the C stack and the PHP stack are fully preserved, this approach is actually very safe and won’t cause memory errors—unless static or global memory is misused. Swoole runs over 1,700 tests on GitHub Actions, many of which involve multiple coroutines issuing concurrent requests. Before testing, containers spin up mysql, pgsql, oracle, redis, firebirdsql, httpbin, tinyproxy, pure-ftpd, and many other databases and servers to interact with code in phpt files. The breadth of these tests speaks to its reliability. Unlike amphp/reactphp, Swoole does not invent new APIs; it reuses PHP’s existing functions. Swoole hooks into PHP streams, the standard library, and other extension functions—such as sleep, stream_socket_client, stream_socket_server, file_get_contents, fsockopen, curl_*, mysqli, pdo_mysql. Inside a Swoole coroutine, these calls are no longer synchronous blocking I/O; they become non-blocking. When I/O isn’t ready, the runtime suspends the current coroutine and uses epoll to watch for readable events, resuming the coroutine only when the operation completes. An example: Co\run(function() { &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sleep(1); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; echo fread($fp, 8192), PHP_EOL; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $conn = stream_socket_accept($fp); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; $redis = new Redis(); &nbsp; &nbsp; &nbsp; &nbsp; $redis-&gt;connect('127.0.0.1', 6379); &nbsp; &nbsp; &nbsp; &nbsp; while(true) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $redis-&gt;subscribe(['test'], function ($instance, $channelName, $message) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; echo 'New redis message: '.$channelName, "==&gt;", $message, PHP_EOL; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; $redis = new Redis(); &nbsp; &nbsp; &nbsp; &nbsp; $redis-&gt;connect('127.0.0.1', 6379); &nbsp; &nbsp; &nbsp; &nbsp; $count = 0; &nbsp; &nbsp; &nbsp; &nbsp; while(true) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sleep(2); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $redis-&gt;publish('test','hello, world, count='.$count++); &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); }); By conventional understanding, this code shouldn’t run: every function that performs network I/O would block the entire process. But in the Swoole environment, the program runs smoothly. We can even modify the code to increase the number of clients by several thousand, and it still runs stably. Co\run(function() { &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; sleep(1); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_client("tcp://127.0.0.1:8000", $errno, $errstr, 30); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; echo fread($fp, 8192), PHP_EOL; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; }); &nbsp; &nbsp; $n = 2000; &nbsp; &nbsp; while($n--) { &nbsp; &nbsp; &nbsp; &nbsp; Co\go(function() { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $fp = stream_socket_server("tcp://0.0.0.0:8000", $errno, $errstr, STREAM_SERVER_BIND | STREAM_SERVER_LISTEN); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; while(1) { &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; $conn = stream_socket_accept($fp); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; fwrite($conn, 'The local time is ' . date('n/j/Y g:i a')); &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; } &nbsp; &nbsp; &nbsp; &nbsp; }); &nbsp; &nbsp; } }); Swoole’s aim is to leverage PHP’s existing ecosystem rather than build a new one. If we were starting from scratch—discarding PHP’s commonly used functions and learning an entirely new async API—why wouldn’t developers simply switch languages? Now that true-async has adopted Swoole’s approach, I think that’s an excellent choice. 2. Where PHP-FPM falls short If all you do is read/write MySQL and generate HTML, PHP-FPM is already superb. If I’m building a web project that only depends on MySQL, I wouldn’t use Swoole; PHP-FPM is the best choice. But many modern web projects need to call external HTTP APIs, and slow requests often render PHP-FPM unavailable, which is frustrating. Async exists precisely to address this. With the rise of ChatGPT, streaming responses such as SSE and full-duplex communication via WebSocket will become increasingly common—technologies that PHP-FPM doesn’t support well. Many developers choose Node.js or Go instead. The influence of Swoole or amphp remains limited; only a small subset of developers opt to stay with PHP for async programming using these solutions. If PHP can adopt true-async or other AsyncIO solutions and provide support for async I/O at the language level, it would be tremendous news for PHP users. In essence, async I/O is a runtime matter—much like Node.js in relation to V8. New PHP syntax isn’t required; Swoole, for instance, adds no new syntax—just some functions and classes—just as fastcgi_finish_request and fpm_get_status are php-fpm–only functions. 3. FrankenPHP FrankenPHP is a wonderful project that uses Go to give PHP additional capabilities, with great room for exploration. In an RFC for a Polling API, author Jakub Zelenka—also a FrankenPHP maintainer—shared a technical idea: consider implementing a goroutine version of the TSRM thread isolation scheme. Each goroutine would have its own Zend VM environment—essentially a goroutine-based php-fpm. I believe this approach may pose significant challenges, especially regarding memory resources. Today, when running Symfony or Laravel under PHP-FPM with 100–200 worker processes, memory pressure is already heavy. If each process consumes tens to over a hundred megabytes, the group can easily use up to 20 GB. With goroutines, if you launch thousands or tens of thousands to handle requests concurrently, memory usage could become enormous. By contrast, coroutines are designed to be very lightweight: a suspended coroutine should retain only the call stack and a small amount of request/session-related memory, while other resources can be shared and reused across requests. This drastically reduces memory usage while still allowing a large number of simultaneous requests. When a request is slow, suspension incurs little cost. 4. Fiber If Fiber and coroutines coexist as execution units, I agree it can be confusing. But the current Fiber simply can’t be used in a Swoole-like runtime with extensive low-level switching. Although Fiber landed in PHP 8.1, Swoole cannot use any Fiber APIs. In addition, Fiber doesn’t fully switch all global memory state—for example OG(handlers), BG(serialize), BG(unserialize)—so it’s unclear whether issues exist there. 5. Golang’s abundance of synchronization primitives Go’s goroutine isn’t purely a coroutine; it’s a combination of thread and coroutine, which necessitates many locks, mutexes, semaphores, and atomics to resolve data races. PHP does not support multithreading. Whether it’s Fiber, Swoole, or any other coroutine implementation in PHP, execution is single-threaded: only one coroutine runs at a time, and until it yields, no other coroutine runs. Therefore, PHP coroutines are not a complex concept but a clear and straightforward one. If the true-async RFC vote doesn’t pass this time, I think we can split the work into several parts, aiming for each RFC to accomplish just one thing. I think the most important task now is to allow coroutine switching in low-level C code, not just in PHP code. Whether we call it coroutines or Fiber 2.0 is fine. On top of that, other work can be introduced via future RFCs to progressively strengthen the design. Lastly, I sincerely hope PHP keeps getting better. Thanks all for your contributions. Open discussion is always beneficial. ------ Tianfeng Han &nbsp; ------------------ Original ------------------ From: &nbsp;"Edmond Dantes"<edmond.ht@gmail.com&gt;; Date: &nbsp;Fri, Nov 21, 2025 03:17 PM To: &nbsp;"Rowan Tommins [IMSoP]"<imsop.php@rwec.co.uk&gt;; Cc: &nbsp;"php internals"<internals@lists.php.net&gt;; Subject: &nbsp;Re: [PHP-DEV] [VOTE] True Async RFC 1.6 &nbsp; Hello. Imagine that we have an application like this. ```php class AuthService { &nbsp; &nbsp; private static ?self $instance = null; &nbsp; &nbsp; private PDO $db; &nbsp; &nbsp; private ?string $sessionId = null; &nbsp; &nbsp; // Private constructor for singleton &nbsp; &nbsp; private function __construct(PDO $db) &nbsp; &nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $this-&gt;db = $db; &nbsp; &nbsp; } &nbsp; &nbsp; // Get singleton instance &nbsp; &nbsp; public static function getInstance(PDO $db): self &nbsp; &nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; if (self::$instance === null) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; self::$instance = new self($db); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; } &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; return self::$instance; &nbsp; &nbsp; } &nbsp; &nbsp; public function login(string $email, string $password): bool &nbsp; &nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; // Find user by email &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $stmt = $this-&gt;db-&gt;prepare('SELECT * FROM users WHERE email = ?'); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $stmt-&gt;execute([$email]); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $user = $stmt-&gt;fetch(PDO::FETCH_ASSOC); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; // Invalid credentials &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; if (!$user || !password_verify($password, $user['password_hash'])) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; return false; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; } &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; // Generate and save session ID &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $this-&gt;sessionId = bin2hex(random_bytes(16)); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $stmt = $this-&gt;db-&gt;prepare( &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; 'INSERT INTO sessions (user_id, session_id) VALUES (?, ?)' &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; ); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $stmt-&gt;execute([$user['id'], $this-&gt;sessionId]); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; return true; &nbsp; &nbsp; } &nbsp; &nbsp; // Return current session ID &nbsp; &nbsp; public function getSessionId(): ?string &nbsp; &nbsp; { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; return $this-&gt;sessionId; &nbsp; &nbsp; } } ``` One day you decide you want more performance and make a single PHP process handle multiple connections concurrently. You wrap each request in a separate coroutine and try to use the old code. ```php $server = new Swoole\Http\Server("127.0.0.1", 9501); $server-&gt;on("request", function ($req, $res) { &nbsp; &nbsp; // create DB connection (just for example) &nbsp; &nbsp; $db = new PDO('mysql:host=localhost;dbname=test', 'root', ''); &nbsp; &nbsp; // get singleton &nbsp; &nbsp; $auth = AuthService::getInstance($db); &nbsp; &nbsp; // read request data &nbsp; &nbsp; $data = json_decode($req-&gt;rawContent(), true); &nbsp; &nbsp; $email = $data['email'] ?? ''; &nbsp; &nbsp; $password = $data['password'] ?? ''; &nbsp; &nbsp; // call old sync code &nbsp; &nbsp; $ok = $auth-&gt;login($email, $password); &nbsp; &nbsp; if ($ok) { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $res-&gt;end("Logged in, session: " . $auth-&gt;getSessionId()); &nbsp; &nbsp; } else { &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $res-&gt;status(401); &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; $res-&gt;end("Invalid credentials"); &nbsp; &nbsp; } }); $server-&gt;start(); ``` What is happening here? Now, in PHP, inside a single process or thread, the same code is literally handling multiple connections. At the same time, there are constant switches between different requests at the points where MySQL queries occur. That is, when the code executes $stmt-&gt;execute([$email]); control is passed to another coroutine with a different $stmt-&gt;execute([$email]); What breaks in this code? Correct, coroutines break the singleton because they alternate writing different Session IDs! And what does not change in this code? The SQL queries can remain unchanged. The first problem with shared memory between coroutines can ONLY be solved by the programmer. Only the programmer. There is no solution that would make this happen automatically. Yesterday we talked about how we can help the programmer detect such situations during debugging. But in any case, only the programmer **CAN** and **MUST** solve this problem. The difference is that you don’t need to rewrite everything else. The focus is only on the issue of concurrent access to memory. The essence of the choice is how much code needs to be rewritten. Almost everything, or only the code with global state. My choice is: it’s better to rewrite only the code with global state — or switch to Go and avoid the pain :) As for the rest, I will write a separate message so as not to clutter things up. ---- Edmond

Edmond Dantes

284 days ago
Hello, Thank you a lot.
> Swoole’s aim is to leverage PHP’s existing ecosystem rather than build a new one. If we were > starting from scratch—discarding PHP’s commonly used functions and learning an entirely new > async API—why wouldn’t developers simply switch languages?
And it turned out to be a very successful solution, which has been proven many times in practice. The first question developers ask when they want to migrate to async is: how much code do I need to rewrite? If the cost of migration is comparable to rewriting the entire project from scratch, then it becomes economically more sensible to hire Go developers and rebuild the project. And PHP’s influence is strong primarily because of the companies that run PHP-based projects. So when a company decides to rewrite a project in another language because PHP no longer meets its technical needs, PHP loses that client. That’s why supporting a single runtime and a single API, standardization, is crucial for the technology’s survival. Those were the economic reasons. There are technical reasons as well. In a server that handles multiple requests within a single thread, it is very important that coroutines do not occupy the CPU for too long. If they do, all coroutines in that process are affected. This means that having blocking operations inside a coroutine nullifies the advantages of concurrency. Therefore, intentionally making functions blocking so that code works correctly with shared memory makes the idea of using asynchronous code **pointless**. This does not mean that one solution or another is 100 percent perfect. Each has its drawbacks. The question is whether the drawbacks outweigh the benefits. Swoole made it possible to see that, in practice, using a unified API provides a significant advantage that makes migrating legacy projects feasible. This means that by voting for an RFC built on these principles, we are not venturing into a dark unknown, but walking along a well-trodden path. ---- Ed

Jakub Zelenka

284 days ago
Hi, On Fri, Nov 21, 2025 at 8:16 AM Edmond Dantes <edmond.ht@gmail.com> wrote:
> Hello. > > Imagine that we have an application like this. > > ```php > class AuthService > { > private static ?self $instance = null; > > private PDO $db; > private ?string $sessionId = null; > > // Private constructor for singleton > private function __construct(PDO $db) > { > $this->db = $db; > } > > // Get singleton instance > public static function getInstance(PDO $db): self > { > if (self::$instance === null) { > self::$instance = new self($db); > } > return self::$instance; > } > > public function login(string $email, string $password): bool > { > // Find user by email > $stmt = $this->db->prepare('SELECT * FROM users WHERE email = ?'); > $stmt->execute([$email]); > $user = $stmt->fetch(PDO::FETCH_ASSOC); > > // Invalid credentials > if (!$user || !password_verify($password, $user['password_hash'])) > { > return false; > } > > // Generate and save session ID > $this->sessionId = bin2hex(random_bytes(16)); > > $stmt = $this->db->prepare( > 'INSERT INTO sessions (user_id, session_id) VALUES (?, ?)' > ); > $stmt->execute([$user['id'], $this->sessionId]); > > return true; > } > > // Return current session ID > public function getSessionId(): ?string > { > return $this->sessionId; > } > } > ``` > > One day you decide you want more performance and make a single PHP > process handle multiple connections concurrently. You wrap each > request in a separate coroutine and try to use the old code. > > ```php > $server = new Swoole\Http\Server("127.0.0.1", 9501); > > $server->on("request", function ($req, $res) { > > // create DB connection (just for example) > $db = new PDO('mysql:host=localhost;dbname=test', 'root', ''); > > // get singleton > $auth = AuthService::getInstance($db); > > // read request data > $data = json_decode($req->rawContent(), true); > > $email = $data['email'] ?? ''; > $password = $data['password'] ?? ''; > > // call old sync code > $ok = $auth->login($email, $password); > > if ($ok) { > $res->end("Logged in, session: " . $auth->getSessionId()); > } else { > $res->status(401); > $res->end("Invalid credentials"); > } > }); > > $server->start(); > ``` > > What is happening here? > Now, in PHP, inside a single process or thread, the same code is > literally handling multiple connections. > At the same time, there are constant switches between different > requests at the points where MySQL queries occur. > > That is, when the code executes > $stmt->execute([$email]); > control is passed to another coroutine with a different > $stmt->execute([$email]); > > What breaks in this code? > Correct, coroutines break the singleton because they alternate writing > different Session IDs! > >
I think you seriously underestimate impact of this in the current PHP code bases where many applications depend on global state. Especially the legacy ones but even the most popular ones. Just look into WordPress which use global state extensively. Now imagine that some popular plugin decides to use async which change some of its globals (like $post or $wp_query) during the suspension of the main code. I would assume this could horribly break things. Don't forget that other code don't have control over the plugin and it might not even know that async is used there. So I'm not sure if this design is compatible with WordPress and similar applications where global state is used extensively. If that's the case, it's of course a problem because those applications (well WordPress on its own in fact) compose the majority of PHP users so introducing something that would have potential to break its code would limit usability of the whole feature and could even lead to loosing more users that we could gain from introducing this feature. So I think it will need to find some solution that will prevent this from happening. I guess there might be few options 1. Disallow suspension of the main sync code which is effectively some sort of colouring. 2. Preventing access to globals from coroutine which I'm not sure is even fully doable from the engine PoV - it would mean some sort of different execution mode that could not use globals (e.g. global keyword and calling some functions that change global state). It would need channels for communications between coroutines. The advantage of such model would be possibility to combine it with threads in the future but I could imagine it could still lead to some subtle issue for years as there is internal global state as well that can lead to some surprises. But maybe that would be worth it. Just some thoughts... Cheers Jakub

Edmond Dantes

284 days ago
Hello
> I think you seriously underestimate impact of this in the current PHP code bases where many applications depend on global state.
Do I really look like someone who could underestimate memory-related issues? :)
> Now imagine that some popular plugin decides to use async which change some of its globals (like $post or $wp_query) during the suspension of the main code. I would assume this could horribly break things.
Or for example, you run an old WordPress on the new PHP 8.5 and everything breaks. Is that a reason not to release new PHP versions? No, I’m not joking. That’s literally the essence of the argument. If something might break, then we shouldn’t do it? This is a common story. There is a framework that is adapted to a technology, and there is a framework or library that is not adapted to it. If a framework is not adapted, you simply won’t be able to use the technology. So why is this considered a problem? However, inside WordPress you will still be able to use coroutines as long as you don’t call WP functions that aren’t adapted. You can. So why is this a problem? I can use AMPHP inside WordPress and break WP. So does that mean we must urgently remove Fiber from the language? Because AMPHP uses Fiber, and AMPHP implements coroutines. And coroutines break WordPress. Asynchrony already exists in PHP. You can write async code today. Which means you can already do all the “horrors” you’re talking about. TrueAsync cannot change that. And no one else can change it either. So why is this being treated as an argument against it?
> Don't forget that other code don't have control over the plugin and it might not even know that async is used there.
Exactly. This means that right now I can use Fiber plus select() to write async code and break WordPress. And I can also write a plugin that divides by zero and crashes WordPress, and WordPress won’t know anything about it.
> So I'm not sure if this design is compatible with WordPress
Yes, WordPress is not compatible with asynchrony. I’ll emphasize again. This is not about TrueAsync specifically. This is about asynchrony itself. Yes, WordPress and Laravel are not compatible with concurrent execution. That’s true. But are you **really suggesting** that because of this, all other applications should be denied the ability to use it? Moreover, would you deny WordPress itself the possibility of supporting asynchrony in future? After all, WordPress can be refactored. It’s not carved in stone. Wouldn’t WordPress benefit from the performance improvements that async provides? Wouldn’t WordPress plugins benefit from being able to actively communicate with microservices and deliver the fastest possible responses to JavaScript? Is this feature really something nobody needs? If yes, then I have no further questions. Async is needed to increase throughput. That’s the purpose. If a PHP project doesn’t need it, it doesn’t have to use it. That’s fine. But there are PHP projects that do need it.

Edmond Dantes

284 days ago
As for next lines:
> 1. Disallow suspension of the main sync code which is effectively some sort of colouring. > 2. Preventing access to globals from coroutine which I'm not sure is even fully doable from the engine PoV
Yesterday there was a small discussion about debugging capabilities in PHP for async. For example: throwing an error if more than one coroutine writes to a variable. Or issuing a warning if someone creates a coroutine inside a function. This can be called an extended debugging mode. Almost no other language has this, and if we implement it, it would be very cool and very useful. We can also come up with other ways to protect PHP users from silly mistakes. For example, blocking async entirely or per namespace. But I don’t think discussing these features is the top priority right now. All of this is nice to have once everything else is already in place.

Bob Weinand

283 days ago
Hey Jakub, On 21.11.2025 12:29:16, Jakub Zelenka wrote:
> Hi, > > I think you seriously underestimate impact of this in the current PHP > code bases where many applications depend on global state. Especially > the legacy ones but even the most popular ones. Just look into > WordPress which use global state extensively. Now imagine that some > popular plugin decides to use async which change some of its globals > (like $post or $wp_query) during the suspension of the main code. I > would assume this could horribly break things. Don't forget that other > code don't have control over the plugin and it might not even know > that async is used there. So I'm not sure if this design is compatible > with WordPress and similar applications where global state is used > extensively. If that's the case, it's of course a problem because > those applications (well WordPress on its own in fact) compose the > majority of PHP users so introducing something that would have > potential to break its code would limit usability of the whole feature > and could even lead to loosing more users that we could gain from > introducing this feature. > > So I think it will need to find some solution that will prevent this > from happening. I guess there might be few options > > 1. Disallow suspension of the main sync code which is effectively some > sort of colouring. > 2. Preventing access to globals from coroutine which I'm not sure is > even fully doable from the engine PoV - it would mean some sort of > different execution mode that could not use globals (e.g. global > keyword and calling some functions that change global state). It would > need channels for communications between coroutines. The advantage of > such model would be possibility to combine it with threads in the > future but I could imagine it could still lead to some subtle issue > for years as there is internal global state as well that can lead to > some surprises. But maybe that would be worth it.
I think you seriously misunderstand this. Async does not allow you to introduce some effects from the outside. You have to actively opt-in to running stuff in parallel. The problematic part, is, when you call code inside of coroutines, which is not safe to be run in parallel with other code. I.e. if you start writing code like `await([new Coroutine(fn() => not_safe_to_run_twice()), new Coroutine(fn() => not_safe_to_run_twice())])` (pseudo code). Where not_safe_to_run_twice() shares some context. So, when you write async code, you have to *know* that the called code is allowed to run in parallel with any other code which you explicitly chose to run in parallel with it. E.g. if you'd run two wordpress functions which don't have side-effects (not even to internal state), then you're perfectly fine doing that. If you run wordpress functions in parallel which may jump back to the scheduler during its operation and leave the internal state in some intermittently invalid form, then yes, then you have a problem. But if your goal is to just do a localized parallelized operation which you know is safe to run in parallel - e.g. some image processing and storing some stuff in the database, within your wordpress plugin, that's absolutely fine. It won't break anything. Essentially: Just don't call stuff in parallel which you don't know is safe to be called in parallel and you're golden. Bob

Edmond Dantes

285 days ago
Hello
> Here again is the link to the instructions: https://wiki.php.net/rfc/howto
I think it was just my internal feeling that voting on a page with a lot of text is inconvenient. So I tried to find some solution in the documentation. That’s why I thought that having the voting on a separate page would probably be the right thing. Anyway, don’t pay attention to it
> On Monday, you replied to a message from Bart Vanhoutte, saying:
Yes, I was unexpectedly surprised that someone suggested the same thing I had proposed a few months earlier. That proposal also included a separate workflow for TrueAsync, which would keep it in a special experimental status for at least six months or a year.
> Let's find a way to get that working group set up, and get people from other projects involved.
There were never any objections from my side. But it seems to me that people have made their choice. And that’s okay. --- Ed

Edmond Dantes

286 days ago
Hello, Derick
> Why is the voting widget not on the RFC page where it belongs?
It was fixed. Please use: https://wiki.php.net/rfc/true_async#voting_choices

Tim Düsterhus

286 days ago
Hi Am 2025-11-19 13:35, schrieb Edmond Dantes:
> According to all previous discussions, version 1.6 of this RFC has > been prepared and is now being submitted for a vote: > > Voting Page: https://wiki.php.net/rfc/true_async/voting > RFC https://wiki.php.net/rfc/true_async > > The vote officially starts tomorrow, as previously announced. >
I'm seeing that you added the full API stub of the proposed API on November 13 (which I'm only seeing now, did I miss an email where you mentioned that you made the change?). I'm afraid to note that the proposed API is in violation of our policy regarding Throwables: https://github.com/php/policies/blob/main/coding-standards-and-naming.rst#throwables. Particularly, there the “base exception” for the Async namespace is missing. I'm only noticing it now with the stub, since the exact class hierarchy was not mentioned in the RFC before. Given the policy violation I believe that the RFC is unfit to go to vote in the current state. Best regards Tim Düsterhus

Edmond Dantes

286 days ago
Hello.
> I'm afraid to note that the proposed API is in violation of our policy regarding Throwables
Thank you. It was fixed.

Tim Düsterhus

286 days ago
Hi Am 2025-11-19 16:19, schrieb Edmond Dantes:
>> I'm afraid to note that the proposed API is in violation of our policy >> regarding Throwables > Thank you. It was fixed.
That is not quite right. The base exception should be `\Async\AsyncException`. I am also noticing that you changed the `DeadlockError` to `DeadlockException` which is a significant change from the previous proposal. The Error hierarchy is meant for problems that are not expected to be caught, because they cannot usefully be handled. The Exception hierarchy is something that should be caught. Given that the RFC says (highlighting mine):
> This condition is called a Deadlock, and it represents a **serious > logical error**.
I believe it should be a `DeadlockError` like it originally proposed. That one should then extend `\Async\AsyncError` and similarly `\Async\AsyncError` should extend `\Error. Similarly for `CancellationError`. It should extend `\Async\AsyncError`. I'm seeing the comment “Does not extend Exception to avoid accidental suppression” which makes sense to me, but the Error hierarchy specifically isn't an Exception. And folks catching `\Throwable` will catch `CancellationError` as well, so I don't see a reason why it should be treated differently in this regard. ------------ Looking at the stub more carefully, I'm also seeing that `\Async\gracefulShutdown()` should be `\Async\graceful_shutdown()` instead (and similarly for `currentCoroutine()` -> `current_coroutine()` and `getCoroutines()` -> `get_coroutines()`). This is something that I could have caught earlier and I'm sorry that I did not. However since changes to the RFC will still be required (which means that the vote will need to be cancelled), it probably makes sense to fix that as well. Best regards Tim Düsterhus

Edmond Dantes

286 days ago
Hello.
> I believe it should be a `DeadlockError` like it originally proposed.
Thank you, this is worth discussing.

Benjamin Eberlei

286 days ago
Am 19.11.2025, 13:35:13 schrieb Edmond Dantes <edmond.ht@gmail.com>:
> Hello all > > According to all previous discussions, version 1.6 of this RFC has > been prepared and is now being submitted for a vote: > > Voting Page: https://wiki.php.net/rfc/true_async/voting > RFC https://wiki.php.net/rfc/true_async >
voted No soley because of the API design problems that Tim mentioned and needs to be addressed before voting. Its not a vote on the merits of the RFC in general.

Bob Weinand

286 days ago
Hello Edmond, On 19.11.2025 13:35:13, Edmond Dantes wrote:
> Hello all > > According to all previous discussions, version 1.6 of this RFC has > been prepared and is now being submitted for a vote: > > Voting Page: https://wiki.php.net/rfc/true_async/voting > RFC https://wiki.php.net/rfc/true_async > > The vote officially starts tomorrow, as previously announced.
This comes a bit of sudden. For sure, you respected the minimum discussion period, though there was still active discussion just yesterday and the few days before. I was also planning to give the now shortened variant of the RFC a thorough read and read the discussions around it. Others seem to also indicate that there were still a couple open points for discussion - thus I would advise you to cancel the vote and discuss these points first, then go ahead with a vote again a week later. My own thoughts on the RFC (from just reading the RFC in depth, without considering prior conversations now): I agree with the choices of separating Fiber and Coroutine, but I disagree that there's a fundamental problem with allowing Fibers in Coroutines. A Fiber is essentially just a stackful generator, from a very-high-level perspective. You allow generators to be used with Coroutines. There should be no fundamental reason this isn't possible. It just might require some refactoring of the code to manage it properly (i.e. just like you cannot continue a generator which is currently active, but suspended in a fiber, you should not be able to resume a fiber which is currently active, but suspended in a coroutine). Cancellation is not an Error - Errors are generally for programmer errors, not for exceptional behaviour. There might be an argument for making Cancellation a top-level item below Throwable, because it's really special. But definitely not Error. (And if you do, the RFC should note a small policy amendment too.) I'm not particularly fond of the FutureLike name. Is there a reason why it isn't simply called "Future"? I know that in an earlier iteration there was a Future class. But Future is only the promise of a future value. It's the callee API. Callers can return specific implementations of Future, like "new Async\Completed(true)", "new Async\Completable" (which has a $future object as part of its API, which then can be awaited on), "new Async\Failed($someException)". A Coroutine should not only offer the current suspend location as API, but the full stacktrace too. (Like ReflectionFiber does.) Async\suspend() should maybe be called Async\yield() - as in yielding control back to the scheduler. Suspension is the observable side-effect of yielding or awaiting. But you don't actually suspend a coroutine, the action of suspending is done by the scheduler. [This also would match terminology used by the linux kernel for example, which calls it sched_yield(). I think it's misnamed on Fiber.] I don't feel strongly about that though. I find it confusing that an "await" operation does not accept Awaitable as its primary argument, but FutureLike per its signature. Why is that? The doc for Awaitable says that there can be await calls on those. But Async\await can't await it? What exactly is an Awaitable then? Would it essentially be the equivalent of an iterable Future (as in: can be awaited multiple times)? Minor nit: Coroutine::onFinally() could be just called Coroutine->finally(). Thank you for working on this, Bob

Michał Brzuchalski

285 days ago
śr., 19 lis 2025 o 13:36 Edmond Dantes <edmond.ht@gmail.com> napisał(a):
> Hello all > > According to all previous discussions, version 1.6 of this RFC has > been prepared and is now being submitted for a vote: > > Voting Page: https://wiki.php.net/rfc/true_async/voting > RFC https://wiki.php.net/rfc/true_async > > The vote officially starts tomorrow, as previously announced. > > For version 1.6 the following important change was made: > All input/output functions are now bound by the shared requirement of > being non-blocking with respect to the process. However, the specific > behavior of each function may (optionally) be defined in separate > RFCs. > > Thus, I/O functions themselves are not part of this RFC, but the main > RFC defines the general way in which they must operate. Thus (as I see > it), the RFC achieves a balance between cohesion and separation of > concerns. > > Since the discussion period has ended, I will not be engaging in > further debate (except regarding the voting process itself). If you > have any questions for me of any kind, you may ask them either in a > separate thread or privately. (This also means that I will not be > answering RFC-related questions in this thread). I will be glad to > hear your opinions and feedback. I wish all participants the best of > luck. > > --- > Best Regards, Ed >
Just a quick clarification regarding my vote. I cast a “yes” because I wanted to express general support for the idea of native async in PHP, not because I believed the RFC in its current form was ready to pass. The ongoing discussion clearly shows there are still open questions to resolve, and I fully respect that. My intention was simply to show encouragement for the direction and acknowledge the work that went into this effort, even if this particular iteration is unlikely to succeed. I’m absolutely fine with the vote continuing or being paused if needed — whatever best serves the process. Regardless of the outcome, I hope the exploration of async in PHP continues. It’s an important topic, and I appreciate the dedication behind the proposal. Cheers,
-- Michał Marcin Brzuchalski

Edmond Dantes

285 days ago
Hello, Michal
> Regardless of the outcome, I hope the exploration of async in PHP continues. It’s an important topic, and I appreciate the dedication behind the proposal.
Honest, open, and clear position. Thank you very much! --- Ed

carlos_silvaaaaa

285 days ago
Enviado com um e-mail seguro do Proton Mail. Em quarta-feira, 19 de novembro de 2025 às 09:38, Edmond Dantes <edmond.ht@gmail.com> escreveu:
> Hello all > > According to all previous discussions, version 1.6 of this RFC has > been prepared and is now being submitted for a vote: > > Voting Page: https://wiki.php.net/rfc/true_async/voting > RFC https://wiki.php.net/rfc/true_async > > The vote officially starts tomorrow, as previously announced. > > For version 1.6 the following important change was made: > All input/output functions are now bound by the shared requirement of > being non-blocking with respect to the process. However, the specific > behavior of each function may (optionally) be defined in separate > RFCs. > > Thus, I/O functions themselves are not part of this RFC, but the main > RFC defines the general way in which they must operate. Thus (as I see > it), the RFC achieves a balance between cohesion and separation of > concerns. > > Since the discussion period has ended, I will not be engaging in > further debate (except regarding the voting process itself). If you > have any questions for me of any kind, you may ask them either in a > separate thread or privately. (This also means that I will not be > answering RFC-related questions in this thread). I will be glad to > hear your opinions and feedback. I wish all participants the best of > luck. > > --- > Best Regards, Ed
Most of the people who advocate for this are responsible for the language's backwardness; they don't know how to conduct a conversation, it's just their opinion and nothing more. Worse, many times they aren't even using the language; they're stuck in the past, like dinosaurs. And the conversation is always the same: "Want asynchronous programming? Change languages, go to Node, switch to Go," but it's not that simple. I'm amazed at how shallow their knowledge is, yet they defend the cause as if they were experts, without even trying to delve deeper into the subject. Those who do this don't lift a finger to move in that direction, and instead they come up with things nobody asked for, insignificant things that only 1% of people will use. They say few people use asynchronous PHP. The adoption is totally different when the language already offers it natively. "Ah, but it will only be used by a small percentage of PHP users," and what about the useless things we didn't ask for that will only be used by 1%? You could argue that it's easy to say, that they dedicate time from their hobby to improving PHP, and I appreciate that, but someone, in this case Ed, also dedicated time to it.

Rowan Tommins [IMSoP]

285 days ago
On 20/11/2025 18:42, carlos_silvaaaaa wrote:
> Most of the people who ...
I'm going to stop you there. Personal attacks are not acceptable. Making assumptions about what other people think is not helpful. Framing the discussion as "us vs them" is not productive. Please, anyone thinking of replying with vague opinions and rants, stop. We're here to make the language better, not to trade insults. Thank you.
-- Rowan Tommins [IMSoP]

Edmond Dantes

285 days ago
Hello all. I’d like to add a few words regarding the 10 percent. Let’s assume there really are 10% of developers in PHP who use Async. We can replace this with some number X. What’s important is how exactly these percentages are calculated. For example, if this X percent is calculated based on the total number of projects, that’s one thing. Suppose PHP is used in 1,000 projects. Out of those, 800 projects are fairly simple, 150 are medium-sized, and 50 are complex. Most likely, async would be used in 200 projects, not in all 800. So if there truly are X% of developers using it, then it turns out this is actually a very large percentage of developers working on medium and complex projects. Do you see the difference? The difference is that medium and complex projects bring PHP developers more money. This means that if the PHP language and its ecosystem cannot compete with Go or Python, then PHP developers earn less because they’re not participating in the projects that generate that money. Even if only 5% of developers use synchronous PHP, that can still be a lot when you recalculate it. And it is precisely the segment of developers working on medium- and high-complexity tasks that pushes the language forward. Therefore, losing 10%, or 5%, or even 2% may actually cost much more for the language’s progress than it seems. Skype and Nokia were once great too, and they also decided not to develop their “1%.”

Jakub Zelenka

282 days ago
Hi, On Wed, Nov 19, 2025 at 1:37 PM Edmond Dantes <edmond.ht@gmail.com> wrote:
> Hello all > > According to all previous discussions, version 1.6 of this RFC has > been prepared and is now being submitted for a vote: > > Voting Page: https://wiki.php.net/rfc/true_async/voting > RFC https://wiki.php.net/rfc/true_async > > The vote officially starts tomorrow, as previously announced. >
Just note here that you effectively started the vote on Nov 19th (votes were already coming in so that should be the official start). There were changes to the policy that were merged on Nov 20th so they should apply from that date. The current text is following: https://github.com/php/policies/blob/c2a1c602deee3988a9ce9cb740169973f5f4a781/feature-proposals.rst . If it was started after that merge, this RFC would be automatically invalid (there wasn't proper pre-announcement and so on) but as it started before, I think it's still a valid vote. It means that you have only 3 days to either stop the vote or you will need to let it finish. If it fails, you won't be able to propose this for for another 6 months unless significant changes are done. I guess it will need significant changes anyway but just wanted to let you know what the options are here in terms of stopping the vote. Kind regards, Jakub

Tim Düsterhus

282 days ago
Hi On 11/23/25 17:59, Jakub Zelenka wrote:
> invalid (there wasn't proper pre-announcement and so on) but as it started > before, I think it's still a valid vote. It means that you have only 3 days
As the author of the RFC amending the policy, I agree. While there was some confusion with regard to the start of the vote (and the placement on a separate page vs the RFC page) it was a valid vote with regard to the previous policy and as such is “grandfathered” in. Vote cancellation will need to follow the new policy, though. Best regards Tim Düsterhus

Edmond Dantes

282 days ago
Hello. To stop the vote, do I need to change the status? вс, 23 нояб. 2025 г., 18:59 Jakub Zelenka <bukka@php.net>:

Edmond Dantes

282 days ago
Hello all. The voting process has been stopped. Thanks to all. --- Ed

Tim Düsterhus

281 days ago
Hi On 11/23/25 19:57, Edmond Dantes wrote:
> The voting process has been stopped.
I moved the RFC back to “Under Discussion” in the overview: https://wiki.php.net/rfc#under_discussion Best regards Tim Düsterhus

Edmond Dantes

282 days ago
Hello. Bob Weinand shared an excellent idea about how Fiber can be used together with coroutines without breaking backward compatibility. It is enough to automatically assign a coroutine to a fiber, and then the code inside the fiber will be normalized relative to the Scheduler. Fiber receives the same properties as a coroutine and can be used as a stackful generator. This change makes it possible not to block Fiber, but instead allow it to work together with TrueAsync and be used in cases where stackful generators are needed. A Fiber blocks the execution of the coroutine from which it was started, meaning its symmetric behavior is preserved. Respect to Bob Weinand --- Ed

Daniil Gentili

280 days ago
Hi all. Reposting here what I already posted (https://github.com/true-async/php-true-async-rfc/discussions/8#discussioncomment-15074303) in the discussion on php-true-async-rfc (the discussion should really move there from this list IMO). Senior software artisan here with 15 years of PHP experience, 8 years of async PHP experience (amphp v2, then v3), 5 years of go experience, 3 years of Rust experience, and much more. My recommendation for async PHP is: - Stackful coroutines - Colorless functions - Existing IO functions should be async - Some memory isolation: shared statics maybe with a separate attribute (i.e. allow fiber-local statics which have uses, but also allow normal statics as is already the case to allow for caching); while getting rid of globals (not statics) in general would be nice, I feel like it would be out of scope for this RFC.   Race conditions are **not** an issue, like they are not an issue in the vast majority of languages, as there is a variety of tools and models to work with them: channels (go), mutexes (go, amphp, rust, C, C++, etc), actor model (any language), etc. This model matches the status quo in golang and amphp v3: from my 8 years of experience in writing async both **business logic** and **abstraction logic** in multiple languages, this is the best concurrency model. Some evidence: I maintain [MadelineProto](https://github.com/danog/MadelineProto/), the biggest PHP MTProto client. MTProto is an async binary protocol over TCP (no HTTP involved), requires heavy caching in order for clients to function correctly, and a lot race-heavy abstractions. MadelineProto is a framework which contains a fully async MTProto client. I migrated MadelineProto to async PHP with amphp v2 (colored, stackless async) in 2018, and then to amphp v3 (colorless, stackful async) in 2022. - The biggest problem while using v2 was the colored approach, requiring the use of await (yield, using await for clarity from here on) every time a function may become async: this is the status quo of some languages like JS, but in reality **it becomes a giant pain when writing a lot of business logic**: not only that, there were constant **breaking changes** every time a method that previously wasn't async (i.e. had no network/IO logic) suddenly becomes async (thus requiring the use of await).   In MadelineProto, I worked around this with a custom coroutine runtime (replacing that of amphp v2), which allowed users to `await` even non-async functions: by then forcing users to always await all methods of the framework, I managed to avoid breaking changes every time an abstraction became async.   Clearly, this was a crutch, made to workaround a big usability issue for end users, caused by colored functions.   Another issue was with the stackless approach, which essentially forced to use of `call` to spawn a new coroutine (creating a new generator) every time when invoking an async function: I again worked around this in my custom coroutine runtime, allowing to spawn a new coroutine simply by using `await` (`yield`).   This in turn led to the creation of a new generator object every time `await` was used: I was also able to work around this, transforming every `yield` to a `yield from`: this reduced the overhead, but it was clearly yet another crutch, made to transform a stackless approach into a more usable stackful approach. - The switch to amphp v3 finally got rid of the colored approach, switching to a stackful, colorless approach.   This was a huge improvement in terms of developer UX: no more worrying about when to use `await`, no more custom coroutine runtimes to implement stackfulness to avoid using `call()` *and* `await` every time when calling an async function, all **without impacting safety**.   Amphp v3's model is pretty close to golang's model, and mirrors go's ease of use. - When migrating the MadelineProto framework to amphp, right away, it was clear to me that with concurrency, race conditions would be an issue.  Amphp v2 already offered synchronization primitives in the form of mutexes, which are more than enough to guarantee safety.    However, I wanted an easier approach for me as the library developer, which is why I, right away, chose to communicate instead of sharing memory, by adopting the [actor model](https://en.wikipedia.org/wiki/Actor_model) for MadelineProto's internal core modules (IO, update handling, any moderately large logic requiring synchronization).    Mutexes are still used for isolated, smaller logic, as they provide equivalent safety with fewer boilerplate compared to channels/actors.    **A shared-nothing approach only adds needless complexity and overhead, when the alternative is simply to isolate logic needing synchronization in a separate actor, or adding a few mutexes in key places, like is already done in many languages like Go, Rust (multithreaded with inner mutability), Java, C, C++, etc** - An issue that is still present to this day in amphp is the requirement to switch to their own IO API in order to write async logic (admittedly a lot better than PHP's stdlib): this is a **huge** issue especially for PHP developers that have only ever used PHP's own stdlib (curl, etc), or common libraries like guzzle.   For example: MadelineProto exposes MTProto events through an event handler, which is a class with appropriately decorated user-defined methods which handle chosen events concurrently (each event is handled a new coroutine).   If users use native PHP functions within those methods, they block execution of:   - All other events   - Most importantly, the library itself, which **requires** the periodic execution of time-sensitive operations in order to maintain connection with the MTProto servers (if MTProto updates aren't acked within a specific time frame, they are either resent or the connection is terminated by the server, plus `ping_delay_disconnect` must be emitted periodically to signal liveliness of the connection itself)   To work around this, I run static analysis on code users write, warning them if they use non-async stdlib functions and libraries in the event handler, suggesting async alternatives (and also offering simple to use synchronization primitives).   To this day, warnings emitted by the static analysis are the single biggest hurdle to thousands of new users of my framework, which simply do not understand why can't they just use `file_get_contents` or `curl` instead of amphp/http-client, amphp/file, etc.   The same thing will happen to PHP, if a split approach is chosen (the old stdlib remains blocking, and a new async stdlib is made)   To those who think making all of the PHP stdlib async will break stuff, I say: **it is a misconception that making all PHP IO functions async will break existing applications**.   Existing legacy frameworks like wordpress will simply keep working as before, using existing application servers like php-fpm or apache.   New application servers based on `spawn` will only be able to use modern, maintained frameworks which use proper synchronization primitives (mutexes, channels, etc), and currently-legacy frameworks can also be adapted with a little bit of effort, just like I adapted the large codebase of MadelineProto over the course of a few months (including both v2 and v3 migrations).   There are also ways to signal the async-safety of libraries: just like in other languages (go, java, C, C++, etc):   - Via a note in the documentation (this class is safe to use concurrently), an example from the go prometheus library: https://pkg.go.dev/github.com/prometheus/client_golang/prometheus, `All exported functions and methods are safe to be used concurrently unless specified otherwise. `   - Via a class attribute like `Concurrent` (closer to Rust's `Sync` attribute, though Rust's Sync [doesn't directly indicate thread-safety](https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=4ddb9fe86e126ee0e9fa76a006d648e2), as race conditions on interior mutability are still possible on Sync structs; in fact, Rust's Sync attribute is more of a **negative** attribute, where its absence signal non-thread-safety, but its presence does not guarantee thread safety, and non-thread-safe structs should be marked `!Sync`, whereas a PHP `Concurrent` attribute could be a **positive** attribute, explicitly guaranteeing thread safety) Outside in PHP: in 2020, I started writing heavily async business logic in Go, and was very positively surprised by the superior developer experience, especially when using async: I was also very pleased to learn that Go *encourages* (not forces) the use of the actor model through channels. In my professional Go experience, I wrote heavily concurrent and massively parallel, high-load services, and golang's stackful, colorless approach. I also used Rust to write async business logic, and suffered from the same issues I suffered with amphp v2: large amounts of boilerplate await keywords, heavily impacting developer experience. To this day, I consider Go's approach superior to that of any other language, confirmed by extensive development experience. Regards, Daniil Gentili.