From what I can observe, PHP is getting more and more a backend framework. Sure, you can still put HTML out but thinking in terms of “microservices” or “backend applications”, this is not wanted anymore and does not scale well. And so, we get a bunch of new and optimized features. One of them is fibers.
Concurrency: Threads, Coroutines, Fibers
First things first: PHP does not support threading as for instance Java does. Threads are scheduled by the operating system, do not have exclusive memory but share the memory of the main process instead and each thread can execute a separate path of code and has its own program counter, stack, and register state. Of course, a main process can have more than one thread.
Threads can be hard to read/understand (especially for juniors) and may lead to race conditions where two or more threads access shared resources or memory in an unsynchronized or unpredictable manner.
Coroutines differ from threads is in the way they are scheduled and the level of control they provide over execution. While threads are preemptively scheduled, which means that the operating system or runtime decides when to switch between them, coroutines are cooperatively scheduled, which means that they must explicitly yield control to other coroutines in order to allow them to execute.
Fibers are similar to coroutines in that they allow for efficient concurrent execution of multiple streams of code. However, while coroutines are typically managed by the application code, fibers are managed by the runtime environment. Additionally, fibers are typically used for lightweight concurrency, such as HTTP requests, database connections, etc. Fibers are also known as “Green Threads” because they avoid the problems mentioned above with respect to threads. The “cleanness” comes from being on the same main process and not spawning sub processes.
Fibers in PHP
Fibers were added to PHP with version 8.1 and – as mentioned above – there is no parallelism like for threads and “everything” happens still on the main process. The primary execution flow and the Fiber do not occur simultaneously. A Fiber can only be started by the main execution flow, and while it is running, it is the only thing being done. If there are other fibers, they will not do work and are “suspended”.
Let’s take a concrete example: Imagine you have two jobs which are implemented as fibers. The first one takes around 60 seconds, the second one only 2. The job scheduler that executes fibers, would realize that the first job needs too long, would suspend it -which means that the entire operation, regardless of what it is, is getting stopped – and continue with the second job. Once the second job is finished, the scheduler takes back the first job and continues where it stopped.
Which brings us to the point: Fibers are an elegant way to let things like HTTP requests, data retrieval from databases and other I/O related operations work smoother and other, much less heavy operations were done faster. Notice that this not mean that things are getting done in parallel or asynchronously. It is still one process and if something is getting processed, evereything else is suspended.
PHP provides a related concept called “generators” since version 5.5. Generators are a handy way to yield back iterable objects which can be used to generate a sequence of values lazily, which can be more memory efficient than generating an entire sequence upfront.
Compared to generators, fibers are much more readable and flexible. But fibers are no comparision with for example coroutines in Kotlin. For having real parallelism/async PHP, there is a long way to go. Fibers are a good step, but rather a beginning, definitely not an end and not even the middle.
After all what I read, I must add that this disappoints me a little. Sure, I did not expect great steps towards having async PHP with just one feature or in just a release. But still, a little bit more. Not just a feature that is relevant for frameworks like ReactPHP or AMP, which are meant to be parallel and had their “own ways” for being async pre PHP 8.1.
However, I do not want to be satisfied with just the theory. That is why I will implement some benchmark tests and refactor existing production code to use fibers.
With Keestash, we run a job scheduler that does some “heavy” work – database/SMTP/HTTP connections are just a few of them. Currently, they run synchronous in a loop. This is where I would like to start, and try out fibers: I want to refactor the job scheduler to make use of fibers. The results will likely be available on the Keestash blog.
If you do not want to miss any update, feel free to subscribe to my newsletter or follow me on Twitter or LinkedIn. If you want to stay up to date regarding to Keestash, you can follow us on social media as well. Just hit the subscribe button Twitter, LinkedIn, Facebook or Instagram 🙂