ReactPHP is a set of packages, which allows writing asynchronous PHP. Machine operations are over 100x faster than I/O operations. ReactPHP wins in performance by not waiting for I/O operations to finish, it will run “the next code” instead. ReactPHP works faster because it will use your thread in the most efficient way. In this article, I tell you more about it.
Do not confuse this with parallel programming, were a new thread is opened when your original thread is blocked by an I/O operation. Parallel programming introduces problems like isolation, thread safety and slow context switching. Most importantly, the program becomes unnecessary complex.
PHP web applications already work parallel, there is a new thread opened for every connection. This way user A is never blocked by operations of user B. ReactPHP can be an improvement to this model, we still keep one thread for every user, but ReactPHP will use this thread at maximum capacity.
This article covers some basic concepts to understand how ReactPHP packages work. Make sure to read until the end, in the last paragraph we will set up a performant websocket server, which is designed to run forever.
“In ReactPHP everything runs asynchronous, except your code.”
This might look like an odd statement, since the previous paragraph said that ReactPHP runs asynchronous. Let me explain how this simple script is interpreted:
- Start of program
- Registration of the event loop and events
- Run the defined Eventloop
- Run the defined Eventloop
- Run the defined Eventloop
In step2 events are registered (in this example time events), in the next steps we will check which events are registered and whether one of them is triggered. The mentioned steps all run synchronously, but there is more to it. We have to zoom in on the “Eventloop” to understand why ReactPHP works asynchronously:
The loop in the code above might run 100x times, but it will only print data after 1 and after 2 seconds. Note that the order of the events does not matter; the event after 1 second will run first, although the event after 2 seconds is defined first. This is why ReactPHP runs asynchronously.
This concept is the same for I/O operations, it does not matter when the operation starts, ReactPHP will only interact with it when it has something (data) to provide.
The check for exceeded timer events is only one step of the event loop, step two would be polling for I/O operations. This is a check to look if one of the earlier registered I/O operations has any results ready. An I/O operation can be many things: operations on a file, database operations, API-calls, printer commands, … And many more!
Almost all I/O operations correspond on the lowest level to streams, an important building block in the ReactPHP library.
In this example, we see 3 important streams:
- ReadableResourceStream – STDIN
- In this example, the input-stream will be whatever the user writes in the terminal (=STDIN)
- This stream functions as a “middleware” between the input- and output-stream, we could do some work here like put the text in uppercase, remove curse-words, … Or in chat context, add the name of the sender to the message. For the example, we will be putting text in uppercase.
- WriteableResourceStream – STDOUT
- This is the destination of our message, for this example it will output the result in the terminal (= STDOUT)
More information about basic concepts
With these two basic concepts, you should have a fond understanding of how the ReactPHP packages work. There are more basic concepts, if you are interested make sure to watch my conference video:
Chatserver using ReachtPHP’s websockets
To understand websockets better, we first need to have a look at a regular HTTPS-connection. When a client would browse to blog.easi.net, this is what the HTTPS-handshake looks like:
- Client requests a connection to the server
- Server will accept this request
- The client opens the connection (connection is established)
- SSL Configuration steps
- The client will request the website blog.easi.net
- Server delivers everything needed for blog.easi.net
- Client will notify the server when everything is downloaded (request to close the connection)
- Server closes the connection
As you may note, there is some overhead before the connection is established and closed. This overhead is not wrong when you ask a whole website from the server. Another important detail, the server cannot initiate a connection with the client. The client needs to start with a request (blog.easi.net).
The most popular use-case for web sockets is a user-to-user chat. Imagine two users would want to have a chat-conversation with each other (let’s say Bob and Alice). Our HTTPS-model would cause two big problems:
- Bob and Alice will only receive messages when they actively refresh the page, or when their browser sends a request to the server. (You could solve this with polling, but this will drain bandwidth.)
- Performance/overhead, the 7-step handshake should happen twice for every message send between the users. The connection should never have been closed, since we know there are going to come more messages.
This is a use-case where websockets come in handy; a websocket-connection will stay open between client and server. The connection diagram will stay the same, only step 7 & 8 will not execute. Since the connection stays open, the handshake should only happen twice (once for Bob, once for Alice).
By using websockets we win time, bandwidth and most important: it will increase the user experience. Bob will receive Alice’s messages instantly and vice versa.
In ReactPHP we could set up such a (websocket-)chat-server in 22 lines of code:
There are 3 important objects to understand in this code sample:
- $server is a basic Websocket server, which in this case is configured to listen on port 6000. This socket-server opens connections but does not keep track of them. This is enough for client - server communication, but not enough for our chat use-case. We need a client – server – client connection
- $limitingServer is an upgraded version of our mentioned $server, the second parameter is used to configure how many connections to the server are allowed. (In this example 10) Another important thing to know is that the limiting-server keeps track of connected notes. At all times the limiting-server will be able to tell us how many open connections there are left.
- $client is in this example is the connection of one user to our socket-server
Except for the mentioned three objects, there is nothing new in this script. There are two events registered which define how our server should react:
- When a user connects (“connection”): Send the message “Welcome!” to the user
- When a user sends a message (“data”): Distribute the message to all the open connection, and send a send-confirmation to the sender.
ReactPHP is a powerful tool, which can increase the performance and reactivity of your application. ReactPHP can be used as a standalone project, or the packages can be installed/added into your existing project/framework.
It is encouraged to use ReactPHP for long-running tasks or tasks which require many I/O operations. It can be a great alternative for Node.js/Go, especially if you know PHP already and the other technologies would be new for you.
- Code: https://com/MarioAquino-MAQU/Common-Navigate-ReactPHP
- ReactPHP docs: https://reactphp.org/
- ReactPHP articles: https://me/reactphp-series
- Feel free to reach out to me for questions:
- Email: firstname.lastname@example.org
- Twitter: MarioAquino_Dev