[PHP-DEV] [RFC] Polling API

Hello,

I would like to introduce a new polling API RFC that is part of my stream evolution work:

https://wiki.php.net/rfc/poll_api

Kind regards,

Jakub

Hi

Am 2025-10-30 22:06, schrieb Jakub Zelenka:

I would like to introduce a new polling API RFC that is part of my stream
evolution work:

PHP: rfc:poll_api

1.

Thank you for the RFC. I've taken a first skim of the proposal and it immediately raised the question of naming and namespacing in particular. Our naming policy at policies/coding-standards-and-naming.rst at main · php/policies · GitHub says that “namespaces SHOULD be used” and given that this is a completely new API, I think we should namespace them.

My understanding is that the proposed API relies on a file descriptor and not something like a timeout. It therefore makes sense to me to put it into a `namespace Io\Poll;` or similar. We would then also have:

     namespace Io;
     class IoException extends \Exception {}
     namespace Io\Poll;
     class PollException extends \Io\IoException {}

The StreamPollHandle method should possibly be placed in the global namespace still, since the stream functions are sitting there - and a SocketPollHandle would of course be sitting in the namespace of the Sockets extension.

2. As for the PollBackend enum.

Is there a reason why this is a backed enum? Generally speaking enums should not be backed, unless there is a good reason for this. I don't think there is in this case - and none of the native enums are backed so far.

3. Exception-wise.

StreamPollHandle::__construct(): This should probably be a ValueError, not a PollException, since passing an invalid stream is a programmer error.

In the other cases it probably makes sense to further split the PollException into purpose-built exceptions according to the Throwable policy at policies/coding-standards-and-naming.rst at main · php/policies · GitHub (“The exception message MUST NOT be the only means of distinguishing exception causes that the user might want to handle differently.”).

As an example PollContext::__construct() should probably throw a BackendUnavailableException or something like this. For PollContext::add() I'm wondering in which cases a handle “cannot be added”. Is this an error situation that users will encounter in the real world? Similarly, when can PollContext::wait() fail?

4. PollBackend

Is the availability of the backends known at compile time of PHP or at runtime only? Depending on that it might make sense to only conditionally define the enum cases, allowing users to check availability with `defined()` or checking the output of `::cases()`. Alternatively, a `public static function getAvailableBackends(): array` could be added.

--------

I'll give the proposal a more in-depth read at a later point, but this email should already provide for some discussion points.

Best regards
Tim Düsterhus

Hi,

On Fri, Oct 31, 2025 at 10:40 AM Tim Düsterhus <tim@bastelstu.be> wrote:

Hi

Am 2025-10-30 22:06, schrieb Jakub Zelenka:

I would like to introduce a new polling API RFC that is part of my
stream
evolution work:

https://wiki.php.net/rfc/poll_api

Thank you for the RFC. I’ve taken a first skim of the proposal and it
immediately raised the question of naming and namespacing in particular.
Our naming policy at
https://github.com/php/policies/blob/main/coding-standards-and-naming.rst#bundled-ewill be alwaysxtensions
says that “namespaces SHOULD be used” and given that this is a
completely new API, I think we should namespace them.

My understanding is that the proposed API relies on a file descriptor
and not something like a timeout. It therefore makes sense to me to put
it into a namespace Io\Poll; or similar. We would then also have:

namespace Io;
class IoException extends \Exception {}
namespace Io\Poll;
class PollException extends \Io\IoException {}

I thought about this and think this might be a good idea.

Just to note internally it might not always be a fd. For example, TimerHandle might implement internal API not requiring fd because some platforms (e.g. kqueue ones) don’t use it but from the API design (and on Linux), it is fd based so putting that to Io namespace might make sense.

The thing is that I also started working on new IO copy API: https://github.com/php/php-src/compare/master…bukka:php-src:io_copy . This is just a stub but it aims to introduce new IO layer (initial mainly for copying but potentially cover more operations) and it should also contain a new IO ring (on Linux based on io_uring) variant that could be potentially also exposed in some form. I will get it to some working form in the coming weeks and then thing how to organise it with the poll.

The StreamPollHandle method should possibly be placed in the global
namespace still, since the stream functions are sitting there - and a
SocketPollHandle would of course be sitting in the namespace of the
Sockets extension.

Yeah those could stay in global and just extend Io\Poll\Handle.

  1. As for the PollBackend enum.

Is there a reason why this is a backed enum? Generally speaking enums
should not be backed, unless there is a good reason for this. I don’t
think there is in this case - and none of the native enums are backed so
far.

I missed that they should not be backed. I just saw enum AdjacentPosition : string in Dom so thought that it’s fine to use it… Will change it.

  1. Exception-wise.

StreamPollHandle::__construct(): This should probably be a ValueError,
not a PollException, since passing an invalid stream is a programmer
error.

This makes sense.

In the other cases it probably makes sense to further split the
PollException into purpose-built exceptions according to the Throwable
policy at
https://github.com/php/policies/blob/main/coding-standards-and-naming.rst#throwables
(“The exception message MUST NOT be the only means of distinguishing
exception causes that the user might want to handle differently.”).

This makes sense and I will introduce more exceptions. I will not use exception per errno but some middle ground is a good idea. Maybe per op exception with codes representing specific errors would be make sense?

As an example PollContext::__construct() should probably throw a
BackendUnavailableException or something like this. For
PollContext::add() I’m wondering in which cases a handle “cannot be
added”.

It fails if the same fd is added. This is also limitation of backends (e.g. epoll does not allow the same fd to be added twice). I got error code for the specific errors so this should be probably exposed as well. It would still make sense to differentiate that it’s an exception for addition.

Is this an error situation that users will encounter in the real
world? Similarly, when can PollContext::wait() fail?

Wait might also fail but less likely.

  1. PollBackend

Is the availability of the backends known at compile time of PHP or at
runtime only? Depending on that it might make sense to only
conditionally define the enum cases, allowing users to check
availability with defined() or checking the output of ::cases().
Alternatively, a public static function getAvailableBackends(): array
could be added.

It’s compile time but not sure if I like exposing enum only if compiled in as it makes the checks harder. This part is not really something that users should use but it’s really more for testing purpose. In reality everyone should just use default Auto… But getAvailableBackends() might make sense even for testing. Maybe it could also have per enum method isAvailable() so user can check that but not sure if it’s needed.

Kind regards,

Jakub

Hi Jakub,

Am 30.10.25 um 10:06 PM schrieb Jakub Zelenka:

I would like to introduce a new polling API RFC that is part of my stream evolution work:

thank you for putting this RFC forward. I missed a build-in unified polling API for a long time!

I just have some minor remarks:

1. Why not use a "Pollable" interface that will be implemented by Socket, CurlHandle, etc? That would allow to directly use the "resource classes" without a step in between.

(The remaining resources should be converted to classes, too, and implement Pollable, obviously).

2. Is there a reason why mysqli (when using async queries) is missing from the Future Scope list? Or did it just not come to mind?

Kind regards
Dennis

Hi,

On Fri, Oct 31, 2025 at 11:58 PM Dennis Birkholz <php@dennis.birkholz.biz> wrote:

  1. Why not use a “Pollable” interface that will be implemented by
    Socket, CurlHandle, etc? That would allow to directly use the “resource
    classes” without a step in between.

I actually planned to use interface initially but there are few issue with it.

  • as I mentioned above, not all handles will always allow using file descriptors (e.g. timer for kqueue)
  • abstract class allow internal api defintion and not calling the PHP functions internally (that’s how the above can be handled as well)
  • it would require exposing the actual fd numbers for streams which after some thinking might not be best idea because it would make easier for people to have two streams for a single fd which might cause issues with filtering, buffering and so on.
  1. Is there a reason why mysqli (when using async queries) is missing
    from the Future Scope list? Or did it just not come to mind?

I put there just those that I actually plan to implement and are relatively straight forward. For mysqli it might require some abstraction to get the mysqlnd stream so would need to check it out first. In other words I haven’t fully investigated it yet. But it should be probably added too.

Kind regards,

Jakub

On Thu, Oct 30, 2025, at 4:06 PM, Jakub Zelenka wrote:

Hello,

I would like to introduce a new polling API RFC that is part of my
stream evolution work:

PHP: rfc:poll_api

Kind regards,

Jakub

I freely admit to not being fully versed in this area, so take my feedback with however much sodium chloride you feel is appropriate.

- Given that a lot of people reading this are probably no more versed in kernel IO polling than I am, a section early on explaining the context of what is even being discussed would be most appreciated.

- I really would rather not add more global constants. Better to at minimum make them class constants of a class that the new API provides. (Maybe PollHandle?)

- Conversely, I'm unclear why the PollBackend is an enum. That implies the list of backend implementations is fixed and immutable, and not extensible now or in the future. I find that claim suspect, as there are six already. (I find that following the 0-1-many rule in most cases pays off in the long run.)

- PollWatcher is created only by PollContext. OK, then please list PollContext first so that reading PollWatcher I have the, er, context for where it fits.

- Speaking of, when there's very long code blocks like this I much prefer to break it up to a block per class, so as to minimize codeblock scrolling. That makes it much easier to read and jump around as I figure out how it all works.

- How would requesting a specific poll backend be helpful, if it varies by OS? If I'm running on Windows, asking for the Linux backend wouldn't help me much, or vice versa. I don't see the use case here. (Ie, please describe the use case in more detail.)

- Who is the target audience for this? I'm pretty sure it's not anything I normally work on, so it's hard for me to judge if certain decisions are good, bad, or "sucky but we have to." Eg, getData() returning "who the hell knows" strikes me as a footgun in waiting, but I don't have enough context to know if that's an inherited problem from elsewhere.

Overall, I think my biggest feedback is "please explain better why any of this matters, because I assume it does somehow but don't understand how from the RFC."

--Larry Garfield

I don’t know what changes this proposal could bring to PHP if the aim is simply to introduce event loop mechanisms like epoll or kqueue.
Why not make ext-uv (libuv) or ext-event (libev) built-in PHP extensions, just like ext-curl, ext-xml, or ext-bcmath?
These event loop libraries have been thoroughly tested across numerous projects, proving to be extremely stable and reliable. They could serve as the foundational infrastructure for implementing asynchronous IO in PHP.


Tianfeng.Han

------------------ Original ------------------

From: “Larry Garfield”
Date: 2025年11月2日(星期天) 凌晨3:24
To: “php internals”
Subject: Re: [PHP-DEV] [RFC] Polling API

On Thu, Oct 30, 2025, at 4:06 PM, Jakub Zelenka wrote:

Hello,

I would like to introduce a new polling API RFC that is part of my
stream evolution work:

https://wiki.php.net/rfc/poll_api

Kind regards,

Jakub

I freely admit to not being fully versed in this area, so take my feedback with however much sodium chloride you feel is appropriate.

  • Given that a lot of people reading this are probably no more versed in kernel IO polling than I am, a section early on explaining the context of what is even being discussed would be most appreciated.

  • I really would rather not add more global constants. Better to at minimum make them class constants of a class that the new API provides. (Maybe PollHandle?)

  • Conversely, I’m unclear why the PollBackend is an enum. That implies the list of backend implementations is fixed and immutable, and not extensible now or in the future. I find that claim suspect, as there are six already. (I find that following the 0-1-many rule in most cases pays off in the long run.)

  • PollWatcher is created only by PollContext. OK, then please list PollContext first so that reading PollWatcher I have the, er, context for where it fits.

  • Speaking of, when there’s very long code blocks like this I much prefer to break it up to a block per class, so as to minimize codeblock scrolling. That makes it much easier to read and jump around as I figure out how it all works.

  • How would requesting a specific poll backend be helpful, if it varies by OS? If I’m running on Windows, asking for the Linux backend wouldn’t help me much, or vice versa. I don’t see the use case here. (Ie, please describe the use case in more detail.)

  • Who is the target audience for this? I’m pretty sure it’s not anything I normally work on, so it’s hard for me to judge if certain decisions are good, bad, or “sucky but we have to.” Eg, getData() returning “who the hell knows” strikes me as a footgun in waiting, but I don’t have enough context to know if that’s an inherited problem from elsewhere.

Overall, I think my biggest feedback is “please explain better why any of this matters, because I assume it does somehow but don’t understand how from the RFC.”

–Larry Garfield

Hi,

First of all, please don’t top post.

On Thu, Nov 6, 2025 at 3:47 AM 韩天峰 <rango@swoole.com> wrote:

I don’t know what changes this proposal could bring to PHP if the aim is simply to introduce event loop mechanisms like epoll or kqueue.

The primary reason for this is to have an internal API that we can use internally. The primary motivation was to actually have a better mechanism for handling signals that can be safely used in ZTS. This is one of the main blocker for introducing coroutine based TSRM mode that could be used by FrankenPHP to use goroutines instead of threads. There was also related timer issue on MacOS and at that time Arnaud came up with kqueue only PoC implentation but we decided that it would be great to have something more generic but we wanted something really minimal without a need for the whole event loop abstraction like libuv or libevent (libev is dead AFAIC) offers. After that I also had few other use cases in FPM as I wanted a bit more generic event handling than what is currently there so I can use it in child before accept. Note that we have got already its own event implementation in FPM so this is more advancement in that area.

So when we have an internal API which we plan anyway, then I thought it could be nice to also expose it to user space so project like AMPHP can use it and effectively drop all other backends. In addition it didn’t look good that the only current polling mechanism for streams is based on select so this is also effectively replacement for stream_select. Again not all users using it need the full even loop.

Why not make ext-uv (libuv) or ext-event (libev) built-in PHP extensions, just like ext-curl, ext-xml, or ext-bcmath?

Those are all optional extensions because they depend on external library. Currently the only way how we could make it always available is to bundle those libraries but especially libuv is quite bloated and adds unnecessary overhead for our other use cases. We actually discussed it internally if we should use or bundled libevent but the agreement between couple of core devs was to create a simple polling API instead so here we are.

I will update the RFC and add more explanation there as this was also requested by Larry and I can see that the motivation and primary use cases for this are not exactly clear.

Thanks for the feedback.

Kind regards,

Jakub