Innovate faster and cut risk with PHP experts from Zend Services.
Beginning to advanced PHP classes to learn and earn global certification.
Help me choose >
Submit support requests and browse self-service resources.
Matthew Weier O’Phinney
Achieving async programming in PHP can be difficult. Luckily, there are emerging technologies, like Swoole, that can help make realizing the benefits of async more achievable.
In this article, we give an overview of Swoole, discuss its benefits and drawbacks, and consider how it compares to NodeJS.
Swoole is a coroutine-based asynchronous PHP programming framework.
It's developed primarily by Chinese developers working on large-scale applications for the Chinese market. As such, it has been stress-tested and proven in high-traffic production environments. It's a technology that you can definitely rely on, and can be exhilarating to work with!
Swoole has a number of benefits, including multiple web workers and separate task workers, coroutine support, and the ability to dramatically increase request ceiling.
As mentioned above, Swoole features multiple web workers and separate task workers, which allows for code deferment. Deferment of long-running processes opens the door to a number of previously unattainable approaches within your APIs and applications, such as deferment of processing to after a response is sent.
Swoole’s Coroutine support means that you can be handling a lot of requests even if you’re doing a lot of expensive I/O (e.g., talking to your database, using the filesystem, making HTTP requests).
The bootstrap is loaded exactly once, so you eliminate that 15 to 25% tax on every request. Because it’s part of your initialization, that means you're using fewer resources, including RAM and CPU, on every request. For some applications, that may mean you need fewer servers, which is likely possible already because of the async runtime.
Speaking of fewer servers, you don't need a web server because Swoole is the web server. You can launch a docker container that only installs PHP, and you don’t need NGINX sitting in front of it.
You don't have to compose NGINX or Apache in the same container, It can be just PHP. And, if you're doing any sort of containerization, having these single-process containers all with one language is really the gold standard.
Anecdotally, members of the Zend Framework and Laminas communities have measured between four and seven times the requests being able to be processed by async server than you can typically get with a standard setup.
You can, of course, tune Apache and NGINX to be very fast, but you can get something even faster with an async server, and Node has proven that time and again, as well.
While the perks listed above to offer sizeable benefits to PHP applications, there are a few evident drawbacks with Swoole.
These drawbacks can include:
As PHP developers, we're used to making a change in our code and then reloading our browser to see what the effect of that change is.
Unfortunately, there's a lack of code reloading in Swoole. That’s because it's running as a long-running process. So, on refresh, it’s using the same code as it had before the change.
There is some hot-code reloading functionality within Swoole, but anything that is required to bootstrap the actual server instance (think application instance, DI container, configurations) itself will now no longer be able to be reloaded.
Because Swoole’s coroutine support isn’t compatible with Xdebug and Xhprof, debugging can be a challenge. You will need to get comfortable with logging.
With Swoole, you can only have one request listener on your web server, only one task listener.
In practice, as you register listeners sometimes Swoole will raise an exception if you register a second listener. But sometimes it won’t, instead writing over the previous listener with the new one.
If you need to be able to handle different types of data with these listeners, you need to add switching logic, somehow, within your listeners.
In Swoole, if you forget to call “$response->end()” the connection remains open until a network timeout occurs. That means the current process remains open, and that means that there's no next tick of the event loop. Eventually, that causes a timeout and it will reap it, but that timeout is still a problem.
So if you can abstract away from this, you’ll save yourself from headaches. (The functionality is necessary so that Swoole knows when the response is complete and can free up the worker to handle another request; however, it’s problematic from a user perspective due to how easy it is to forget to call it.)
So it is a really useful and expedient feature in a way of doing it within the Swoole runtime, but if you can avoid this in your own code, that would be better.
The “$response->end()” method is one example of the non-standard request/response API in Swoole. It does not follow the PSR-7 specification (HTTP Message Interfaces for PHP), or even any of the framework implementations, such as Symfony’s HTTPKernel or laminas-http.
So, if you’re going to write directly to Swoole, but you still want to use your framework, you will need to adapt – and that can be an issue.
Swoole provides very similar features to NodeJS. It has an Event Loop, provides async HTTP, network, and socket clients, you can create network servers, the list goes on. But how is it different?
Maybe the biggest difference between Swoole and NodeJS is that Swoole offers coroutines. More, it has coroutine support for built-in clients like TCP and UDP. Coroutines allow internals of the language to be processed asynchronously, while allowing code to be written as if the execution was synchronous. Typical async coding requires passing callbacks that will execute when the async process is complete, which can lead to convoluted code for aggregating results. Coroutines greatly simplify the consumption of async code by making it appear the same as normal, synchronous code.
Since the Swoole coroutine support includes most TCP/UDP operations, if you are making a network call, say, making an HTTP call to another server, or you are communicating to Redis using TCP operations, you will benefit from coroutine support out of the box.
Swoole also differs from Node in that, by default, it spawns multiple workers per server, and spawns a number of workers that is scaled to the number of cores present in the servers. So, by default, it's operating at its probably best performance.
Having multiple workers also means that if one worker is blocked on a long process, there's probably another worker that can process it. Each one of these, in turn, has its own event loop, meaning each can defer execution or spawn coroutines, greatly improving your application performance.
On top of having multiple workers per server, Swoole can spawn Task Workers separate from the web workers.
If you want to defer something and not block web requests on it, knowing that you don't have to wait for the results of it, you could spawn a task instead, and it will go to the task worker pool, allowing you to process it that way. This means truly non-blocking operations in your web worker pool are possible!
Lastly, it’s easy to daemonize a Swoole web server, while the same process in NodeJS is more difficult, and requires additional dependencies to realize.
This is particularly useful if you are running multiple servers running on the same box. If you're using something like Docker, you don't need to daemonize it (and the default is to leave the process in the foreground), but having this daemonization support built in and easy to use is a nice perk for Swoole.
In this article, we looked at some of the benefits of Swoole, as well as some of the downsides. In our next article, we’ll look at how developers can configure a basic web server with Swoole, then follow that with a guide to pairing Swoole and Mezzio for async programming in PHP.
Need support for your enterprise Laminas project? Zend can provide cost-effective, long-term support and guidance for your application and team.
If you can’t wait for the blogs, you can see that information presented in this on-demand webinar.
Looking for additional reading on Mezzio, Laminas, or asynchronous php? These resources are well worth your time.
Development Lead for the Laminas Project, Zend by Perforce
Matthew began developing on Zend Framework (ZF) before its first public release, and led the project for Zend from 2009 through 2019. He is a founding member of the PHP Framework Interop Group (PHP-FIG), which creates and promotes standards for the PHP ecosystem — and is serving his second elected term on the PHP-FIG Core Committee.