Websockets in PHP

 5 years ago 15,556 views
Presented by: John Fansler (@johncurt82)

October 18, 2018

Long gone are the days of serving up stale HTML pages. Users want to see their data in real time – not some old (or even slightly old) version of the data! Older technologies just don’t do this very well.

Using WebSockets, we can deliver near-real-time data to our users while reducing the bandwidth on our servers at the same time. Although this technology is not new, its application in web development is.

Let’s take a deep dive into the world of WebSockets – how to create and use them in PHP. We will look at what they are, what they can and cannot do, and then dig into some real-world code along with how to leverage it in a production environment.

John is a Sr. Application Engineer with Vya, tech blogger, freelancer, and dog lover. He began his “career” in the early 90s when, still a child, he wrote a web server in BASIC. Since those days, John has been a lover of all technologies that relate to the web. He started a company while in High School in the late 90s doing web design and hosting for small companies, where he began using PHP to deliver dynamic content for his clients. Later he went abroad to see the world, freelance, teach English, and study Application Engineering at Bauman University in Moscow, Russia.

John is currently located in Las Vegas, NV, and loves spending his time hiking around the Valley with his dog, as well as doing various coding projects. You will find him at any number of meetups, including PHP Vegas, learning and speaking about coding.

Transcription (beta):

Okay. So I wanted to take a minute to just kind of introduce what we're looking at. Since Mike already did a great job, introducing me, I'll skip over all of that. But the one thing that maybe I should mention is that I done a lot of traveling. So you know, I have a master's in science and software engineering, but it's not from here in the United States of America. It's actually from from Bauman, Moscow state university in Moscow, Russia. So I actually spent 10 years over there learning, traveling, figuring out what I wanted to do with my life. That brought me to the concept of creativity and how while I'm a very logic driven person. I'm also a very creative person. I love to make music. I also love to make software. I love to solve problems. And John Romero, Romero if you don't know who he is, he created Wolfenstein, 3d doom quake has this great quote.

You might not think the programmers are artists, but programming is an extremely creative profession. That's logic based creativity. And that's what I want to challenge you to today is to think a little bit outside the box and take some of our great logic with BHP in the back, kind of the backend and the way we normally see it and look at it in a slightly different way. So we're going to totally throw everything upside down and you use PHP for web sockets, which may seem counter intuitive, but it does work. So what are, WebSockets four basic concepts about web sockets that make them great. In my opinion, first one is that they're really, really simple. It's just messages going back and forth as a developer. You don't have to worry about a lot of different things happening. It's just messages. There are actually two channels for communication.

There's one coming from the server to the client and one going from the client server, which gives us a full duplex connection. That means that the server at any time, without respect to what the client's doing, can send a message to the client and the client can do the same to the server. So it becomes a very asynchronous environment, but it also becomes one where the server can push messages real time out to the clients, which is becoming more and more important in the modern age. So the other thing about web sockets is that they're persistent. Once you open a connection with a web socket you don't have to keep opening more connections. The idea is that you open it and then you have this two channel full duplex, simple stream of messages going back and forth, and you don't have the overhead of creating new connections.

Websockets works over HTTP. So you connect just the same as you would with any other HTTP connection. And there's an upgrade command sent to the HTTP side from the client that says, I want a web socket and those server can then respond at that point. The connection just stays open. There are some things that go along with that. It can be closed and there's a heartbeat where you can send a frame back and forth every so often, which is recommended. And I'll talk about some of the reasons why a little later, but you have this persistent, simple, full duplex connection. The other thing that's really important here is that web sockets are standard RFC. 64 55 was a standardized back in 2011. So it is actually really, really well adopted today just as of a couple of days ago Courtney, the Kamai news.com web sockets has a 96.5, 8% global usage.

So almost anyone that's connecting to your site has the ability already with their browser to use WebSockets. If you've included them in your site, you'll let us that offer mini is the one that's red. And I can tell you that, you know, a couple of things about opera mini one, it's only, it only has a global usage of 2.3, 3%. It was last updated or released on March 15th, 2015. But the reason that it doesn't work with WebSockets is the opera mini is actually talking to operas servers in the background and their servers are talking to your server and then compressing that data and sending it back to their browser. So it's really only used for mobile communications and by a smaller portion of people who are really wanting to keep the bandwidth very low. So as things have progressed, this has become a smaller and smaller concern, but that does still exist.

But if you have people that are using opera, mini web sockets will not work for you, but as you can see, I E starting from 11, it does not work with I E versus prior to that, there are some caveats there, edge Firefox, Chrome, safari iOS and Android, both as well as the Samsung internet browser, which I've used in the past at some point or another. They all work with web sockets. So wait were to think about a site where we want some sort of real-time communication, where as soon as something changes on the server, we then have the ability to see it immediately in the client. What we've typically done in the past is something called polling, where we open up multiple connections over and over and over asking the server. You know, the client says, are there any new messages? It opens a new connection.

The server responds, no, the connections closed. Then we open a new connection, any new, no, and this just keeps happening until finally the server says, yes, there's a new message and gives it. So you have all of this overhead. I started working on a project several years ago. I think it was about five years ago now which had this concept of like a real-time chat. And we were just running like a dev environment, just kind of leaving things on with AWS. And I can tell you that, you know, the bill just by leaving that on with one client, talking to the server constantly for an entire day, ended up at that time, costing us about $10. That was just the overhead of these. Are there any new messages? No. Cause it wasn't a valid service yet. It was just a, a development server that was running this since I left the client running and asking all these questions and costing me about 10 more dollars, that was just one client.

So there has to be a better way. That better way is web sockets. With web sockets, you start up the beginning, you establish the connection and then all you have is a possible frame here and there. But for example, with like a chat service, we were pushing the limits, trying to determine how often we need the check, because we wanted it to seem real time. So if we check every three seconds, for example, we open a new connection. Every three seconds. We have lots of connections happening in the course of a minute. If you have already established a connection in WebSockets, and let's say that you have a heartbeat happening every 50 seconds, that's one frame, which is less than the overhead for actually creating a connection every 50 seconds. So if you're going from three seconds to 50 seconds and going from multiple frames down to one frame, you're saving a lot of bandwidth, a lot of overhead and saving money.

So what happens now is as soon as the server has a message for the client, they can say, here's a message because we have that full duplex by directional messaging happening. So the server says, I have a new message. The client deals with that the client, then if we're talking about a chat system can send something back and the server then can send that immediately to the other person who's logged in. So we just have this constant ability to send messages, either direction, wait, start looking at this. You know, I think all of us probably have a pretty good grasp on Ajax and how that works and you know how to make those connections. Websocket connections work ever so slightly different, but I think it's really a simple way to do things. Javascript has it all built in. If you say, you know, give me an example, socket here, new web socket, and then you put in the URL of the web socket.

You've a connection that will negotiate the connection. You can check on this variable that gets returned. You can check that object to see if it's connected or not connected, and you can even set up different things to respond to when you're ready to send a message to the server, you just use the syn the send method on that example, socket, if you want to be able to respond to messages from the server, there's an on-message that you can set to be a function. It receives an event which has data as one of its properties. So you can get the data and respond to any message that you get super simple on the JavaScript side.

So for the server side, I'm going to be showing an example with ratchet, which is built on top of react PHP, not to be confused with react the front end, but react to PHP, which is an asynchronous framework of sorts to help facilitate asynchronous communication, which running a server in PHP, not a script behind the server, but the actual server with PHB requires a lot of asynchronous communication. And that's exactly what we're going to be looking at. So, you know, when you start your new project, you would simply install ratchet with composer. Composer requires sea boat and ratchet a big shout out to Bowden for his work and ratchet and releasing that. Then we set up a server script. The server script will be the entry point to run a server. That's going to listen on a port for incoming HTTP requests and allow them to upgrade into web sockets and then give you an interface to work between multiple clients with one server.

Just for this example, we're going to assume that we're using Jason for all of our messages, and I'll be showing how to make a router that can dynamically find public methods. Just kind of doing all of this from scratch to show how all of these things work together. The other reason we're kind of doing everything from scratch is a lot of things when it comes to the asynchronous world are not yet developed. So for example, later on, I'll be showing you something that I developed for asynchronous my SQL communication, which if you don't want to use my SQL, you want to use pro stress or something else. That's perfectly wonderful, but you'll have to come up with your own way of talking to that in asynchronous way. So let's look at how we set up this basic web socket server and this server instantiation that we're going to be using three different components from ratchet.

The first one is their IO server, which just does the basic communication at the port level. It listens on a port and passes that information on or gets information to send out to a court. That's all it does. I have a server with the IO server. There's also an HTTP server, which I think is a little bit more obvious. It deals with the HTTP side of things. What does an HTTP request, what should be done with it, et cetera. And then we also have the web socket server provided by ratchet, which handles the the upgraded connection and the back and forth with messages. The, my app router, as an example, here is going to be our app. And that's what we will do all of our stuff with. Obviously we're going to load our composer auto loader. And then it's really as simple as this, we generate a server using the IO server factory into that.

We pass a new instantiation of the HTTP server and to that a new instantiation of the web socket server and into that our router, which we'll be looking at here in just a minute, exactly how to set up that router. You'll notice that there's one other thing to pass in. And that is the port that you want your web socket to run on. It's recommended that the public port be either 80 or 80 AB though, there is no standard for what port to use for web sockets. They work on any port. The reason that some people suggest 80 or 80 80 is that those ports are generally left, open and available if not blocked by various organizations proxies, et cetera. So generally it's open communication on those ports that's available. So I have a tend to always use a different port like 8,100 when it comes time to make it public to the world I'll still be running 8,100 on my server, but my load balancer, for example, will be listening on port 80, 80, 80.

Once you've created this server, which in the background has react PHP running with a loop. You then start it at this point. It kicks off this IO server, which opens the port listens and then passes anything, either direction up through the stack to your router and from your router. And back down to the port through the IO server when you do this server start, what it does is it takes the loop. There's a loop that's created in the react PHP portion of everything that is basically a while loop while true do this. And it just keeps going over and over checking for the next thing to do. That loop has all kinds of things that we can do with it. And we're going to look today at, so the things we can do to tie into that loop ourselves not do everything directly through ratchet, but still use ratchet as the base of our website and servant about this.

My app router that we're going to create you don't have to go the route of making a router here. This is just one way of implementing it. Anything that implements ratchets message component interface will do the trick. The message component interface has four basic functions that we need to pay attention to. The first two are on open and on close. These functions are going to be called these methods whenever someone connects or disconnects from our web socket server, obviously when someone connects, we're going to get that, that connection interface, which is an object, which allows communicate with that, with that client on open is generally a good place to for example, store that so that we can access it later. Let's say we want to send a message to all the clients that are connected to us. We'll have that stored so that we can access it again in the future and send a message to it.

Then in the, on close this call, this gets called whenever someone disconnects or is disconnected or there's some sort of an error, maybe their connection went away, maybe the the load balance or disconnected them because there wasn't specific Tivity. Anytime that there's a close, you get this odd close call with that same object being passed in. So then you can go and do cleanup. You can remove them. Let's say it was a logged in user. We no longer want to point that log in to this client. Or, you know, if we are going to send out a group message, we no longer want to get this out of our, let's say array of connections. We won't be using an array here. We'll be using an SPL object storage, but same idea, something that we can loop through. I want to be able to clean that up. The reality is that memory management is going to be one of the most difficult things to to handle. When we start talking about an application, that's a long running application, like a server. So it's really important that we clean things up everywhere we go. So this on open and on close gives us that ability to clean things up when someone disconnects, which is really important, because otherwise we start to run out of memory and our server just dies.

The next thing that a ratchet gives us an on error. Every time it calls down in with a message or a cold open or closed, if we throw an exception, it will come back right here to this method on error, which gives us a link to that person or that client, as well as the actual exception. Now, as I'm doing this, I'm going to assume that this is actually going to a log things, but B also notify the user that maybe an error has occurred. If you're really using this on Eric's would notify the user. We need to make sure that we're sanitizing things so that if we get some sort of a Jason error or some error from other code, we don't want that to bubble up here and pass a technical error. So definitely want to sanitize what you, if you're using this to send a message to the client sanitize that for sure. But this gives us the ability to do all kinds of things that we might want to do when there's an error that happens.

And then the one that you're going to be using the most is this on-message method. Now, on message just gives you the message. So if we're talking about Jason messages, it's going to be the text of the Jason string. If if you're not using Jason, let's say, or using SML, it'll come in an XML or whatever you send from the client or they send is what the server receives. So you're going to get a, the object, which is the same as this con the connection interface. And you're going to get the message. All of these things together are what you need to have a message component interface. And with that, you can create direct a direct service in this on-message. Or you can use this on message to be the entry point to some sort of a router that would determine what other methods to call, which is what we're going to be looking at specifically right here.

So in our router, I'm going to assume that we're going to have different controllers. So I'm going to store those controllers in an array that way I have the ability to only load them once. This gives us a little bit less overhead in the time for loading. Also sometimes when we load something, we might need to make a database query. This gives us the ability then to not have to do that every time, if you need to do something every time or more often that should be handled as well. And you don't have to store the controllers once they're open, but that's how I'm going to do it here today. The other thing we're going to have is a a list of all of the connections. So when we get the on on open, we're going to save that into clients.

So in our constructor, we're actually just going to set up this SPL object storage. What SPL objects towards gives us over an array and some ability to grab objects that are the same and remove objects that have the same objects end up being pointers in PHP. And they work a little bit different than other variables. And we're going to use that to our advantage and have clients be an object, as opposed to an array. That way, when we pass it around somewhere, we don't have to do it by reference. We can just pass it around and know that whoever's received this clients object. They have the same object that we have elsewhere. So when one of them updates the other one updates as well. So SPL object storage gives us an easier, faster way to kind of make all of that work together on open.

We're just going to attach the connection into our clients and on closed, we're going to detach. That's really all we have to do in this simple example for the on open and on closed. If you're doing something where you want someone to get something immediately, when they log in, you would do that right here in the autopen. You could say the connection interface, you just send data directly to them. There's all kinds of things that you can do with this with these methods next up, let's look at the controllers. So the controllers let's just make a private function here called get controller, which takes a class name. What it's going to do is check to see if it's already got that class. If it does, it's going to return that, that controller, if it doesn't, then it's going to see it for the exists.

And if it does save it in the cache and return it, otherwise, it's just going to throw an exception, which will drop everything and take us to that on error method with a route not found message, which maybe is what we want to return. Maybe not for a bunch of developers. I think that's a great one route not found. So the next thing that we have is the on-message. Now the on-message, I'm really, really excited about PHP 7.3 and this new Jason throw on error because Jason decode doesn't throw it just returns. No, if you give it invalid, Jason. So if we receive a message from our client and it's not Jason, we don't want to do anything with that any further. So as you can see, I've decoded it. And then if that decoded information returns now, normally at this point, you would check to see if there's a Jason error.

If there's not a Jason error, the Knoll was what was sent and that's a valid object, but for our purposes, no is never going to be okay. So I'm not even going to bother checking to see if there was a Jaison error. I'm just going to say, this is an invalid message. You can't send that once we have gotten and decoded our Jaison string into an object, we can check to see if there's a route we can ask that get controller method that we just created for the controller. If not, we can route not frown. And then this is where we kind of route that to a method.

First, you want to check that there is an action that's been sent. So in our example, we're waiting for adjacent message that has route, which is the class that we're going to call in. And then action, which is the method we're going to call. And then any other data we're just going to pass in directly. So we want to see that there is an action that it's a string. We want to stop people from calling our constructor, and then we want to make sure that it exists. If the method exists, then we can do something with that method. Otherwise it's an invalid operation. Now, one thing we have to be careful of with methods is that they can be public or protected or private. And if you try to call a protected or a private method from outside a class, you're going to get an error.

And unfortunately that error would kill our server. It would just totally stop it. It would die. So what we want to do is use the reflection method here to just pass in that controller and ask it to make a reflection of the action that we're interested in. Then we can check that reflection to see if that method is public. If it's public, then we can call it directly. And I know this code gets a little bit harder to read because I was running out of space. But if we look right in here, you can see that I've put message Jason action in brackets. And by putting it in brackets, I'm actually calling whatever's in there, dynamically on the controller. So we've checked to make sure that we have a controller we've checked to make sure that our controller has the action that we want. It's public. And now we're calling it dynamically. We're passing in the connection. So it has access to who called it. And the object that we created from the Jason, all of that together gets, it's a pretty simple router that allows us to take hands-off and now just concentrate on what we're going to do in our program. Now we get to the fun stuff.

So responding. We need to be able to do something when a client talks to us. So how do we set that all up in, you know, let's start with a core controller, just a basic, these are the things that all of our controllers are going to have. We'll have a clients, which is going to be an SPL object storage, which got passed in if it existed in the previous slide. And then just a couple of helper functions. One is a sin function to just reimpose my object and send it to the connection. You'll notice here that this connection interface just takes the send method with your text string, whatever you want to send on its way. So we're just going to re Incode our object or our array into Jason text and send that directly. And then a send to all would just loop through all of the clients and send the message to all again, Jason coded. Now this time I've Jay Simon could have been outside of the, for each just so that we're only in coding at once and we have a little bit less overhead. You really don't want to do many things that are going to slow stuff down, because if your server's busy doing one thing, it can't be doing another thing. One of the issues with running this in PHP is we don't have very many threads to work with. We have one thread for direct PHP code to run it.

Next up, we have our class. This is the first class we're going to make. Let's just call this controller chat back. We can create a public function. Hello. Now someone sends a message that says, route is chat back, and action is hello. Then we're going to respond back with an array, route action, and a message that says hi, there really simple server can now respond to the action of hello in the route, chat back a little bit more. We can do a hello name, which is going to now take that message that we're getting and look to see if it has a name property. If it doesn't have name property, we can throw an exception. You didn't pass in your name. If it does, then we can even call it private function, build name response. I've really only done this to show that we can have private methods.

If someone tries to do route chat back action, built, named response, they're going to get an error that they can't do that, but it's not going to kill our server. Because in that previous, in the, in the router, we have the reflection method. It's checking to make sure that anything that it tries to call is indeed public. So really simple, really basic. This is how you would reply or respond to the client's message. The client sends you something. You can do something with that information. It comes in on this message. You can either send something back to them, or you can you know, save some data check the database, whatever you need to do, based on what they've asked.

Next thing you might want to know is how do I initiate from the server a message to the client when the client hasn't asked for anything? Now there's a couple of things. One of them is that when we created this response that we have it's in this core controller and the core controller has the ability to send to any connection. Now, you notice that there's a property of clients and we can go to that client and we can ask it to give us a list. We can go through that list and we can parse them. We can save another SPL object storage that has been linked based on their log in. We can add additional data so that we can find those clients that we want to find, and we can send them data. That's one way that you can do something with, you know, two clients without that client messaging you, but there's also the ability to do something without anyone asking you for anything, totally just initiating something from scratch.

And this is where we get into dealing a little bit outside of ratchet and dealing more with the loop in react directly. So we can do that outside in that server script that we created recall that initial server script, we had the server equals, I go server factory. We pass some stuff in there and created our server. Then later we called the server start well in between, after we created the server. And before we started, we have access to the servers, which is a ratchet server. We have access to its public properties. One of those is server loop, and that loop is the react loop. So we have access here to put things in the loop directly. Now, in this case, I'm adding a periodic timer. You can add a periodic timer, you can also add a timer. There's also some things with streams and other things that you can get into with reacts directly, but probably the most important here are either periodic primer or timer. So regular timers just wait so long and then do this thing. But the periodic timer is wait so long, do this thing. And then start over again, wait again, and do it, wait again and do it. So in this particular one, I've set up a 10 seconds, periodic timer that cost this anonymous, that creates a message. That's J something coded that literally just says, generic message every 10 seconds. And then I can go through all of the clients in the app and send that message. Yeah.

Now you'll notice that I have this anonymous function with the use construct and inside of views I have and string app. So outside back when we were creating that server, we also instantiated a new router, right? So we don't have to do that right in line. We can also do that before we create the server. So we have access to this application variable by putting the ad in front of it and not passing that in by reference. So at passing it in by reference, no matter what happens to our application, it's always going to track. So even though outside of all of our server, in a totally different scope, we have a loop that's running every 10 seconds. It has access to the app. And inside of that app, it has access to the clients that are updated and kept updated in real time.

So now we can loop over those clients. You could also parse your client's parse, who's logged in, do whatever you want, be able to send a message to whomever you want in the client you want, based on any periodic timer, another thing that you would use this periodic timer for something that you will want to be able to do, for sure, in any PHP application has to do with my SQL being able to clear the database and then set up some sort of function. That's going to take what happened in the database query and do something with it because remember we really want to do everything asynchronously. So when we're moving into asynchronous SQL commands, it works a little bit different than the standard way of, you know, making a new, my SQL and then running a query on it. And then, you know, that returns a result that time in between the running the query.

And when you get the result while your query is running in standard synchronous BHP, you know, if you have a query that takes 10 seconds or heaven forbid 10 hours to run, your script is going to sit there. And for that amount of time, doing nothing, including anything else that might be running on a timer, doing nothing, waiting for the result to come back. And that's exactly what we do not want. But fortunately there is the ability with my SQL I to do asynchronous queries. So if you're really interested in this, you want to look up my ask ULI hole, that's where you get the most documentation. That's where you get full examples. If you look up the other individual pieces of making a, my SQL asynchronous query it's really vague even knowing to use poll, it's still a little bit big. So we'll take a look at that right now, how that works. One thing to note in order for asynchronous to work, you have to be using the, my SQL native driver. That's what the, in D in my SQL in D stands for native driver. That driver does work with asynchronous calls. The other one does not. So you'll really just have to make sure that in your PHP setup, you've included in D and that that is ready to go.

The SQL pole works in a very strange way, and that takes three arrays. And those are rays are arrays of my SQLI connections. So when you say it new, my SQLI, you've got this link. You put all of those links into an array, and then you pass that array into my SQL live poll. And what it does is it sorts them kind of, it's not immutable. It's what you pass in there. It's literally going to delete stuff from your Ray. So on the other end of running it, it's going to return to you the number of connections that are able to be reaped. And then it's also in the meantime, going to mess with those arrays that you sent into it and change them. The first array we'll look at what the rates are for, but just know that they're not immutable. You have to create these arrays separately differently. Every time you call my SQL, I pull because on the other end of it, your array has been changed completely.

So this next example, yes, it's poor poor code. It has a blocking, never ending loop. It's really easy for this to not work, right? I know I'm going to bank on the fact that these queries with sleep three is wait and test. This test are never going to fail. So we're never going to have any errors, and we're never going to have any rejections from the server. If we did this loop would never stop normally without using the, my SQLI async option. As you see on the third line there with link one query select, and then at the end, my SQLI underscore async. If you don't put that master line, async this query will block and it will sit there stopping everything until it's received a response from that query, which in this case with a select three means it's going to take three seconds.

Now, link to, if you take out the minuscule, I async is going to take six seconds to run. So if you take out those masculine's Eysenck's and you just run those first four lines, it's going to take a total of nine seconds to complete the queries. But by doing it with the mind through ally async, it's going to take a total of six seconds to perform both because at the first three seconds, both queries are actually running. So this is what asynchronous gets us. So if we create two separate links and run two queries on those two separate links, we can then put them in an array. You see that there on the line that says all links equals and then array with link one, link two. We can count them. We can check to see how many we've already processed and compare those.

That's what I'm doing for the do while loop. But then that, you know, inside this do while is the most important part. That first line links equals errors equals reject, equals all links. The first time I saw this notation, I have no clue what it was even doing. Why would you have three equal signs? Well, it turns out that when you have three equal signs, the one on the far right is going to feed into the one on the left and the one on the left and into the one on the left. So when you're done here, you have three arrays that have been copied from all links. So links equals all links, errors equals all links and reject. People's all links, all stuck in one line is exactly the same as links equals errors equals reject equals. Yeah, it's very important that these be standard straight up arrays, because those are rays again, are going to get messed with.

So when we pass these into my school, I pull, it's going to return the number that are ready to be read. So after three seconds, it's going to return a one and it's going to return zero until we've reached six seconds. And then it's going to return a one. So if it's not returning something, that's, you know, positive, then we've got a false statement. We're just going to continue. Start the loop back over, just start the do, run my SQL. I pull again, create those copies. Do it again. We're just going to sit there and check and check and check. Now this part of it is blocking, and I'll send you in a minute, how we're going to do this without blocking, without our do while loop using the react PHP that's built in the print, moving on is just so that you can see when it's doing something. And then this first array links originally, it was all links.

But now that we've run my SQL I pole links is now just the links that are ready to be reaped, reaping. That link is how you get the result. So if we pull in the result and it's an object as opposed to false, so that worked, then we have data coming back. So if you actually run this we're going to get three tests and then six tests as well as the moving on both times in between, and one is going to happen after three seconds. And the next one is going to happen three seconds later because we've done them asynchronously. And then it will stop assuming nothing went wrong and we didn't end up in a forever loop.

So when it comes to our actual WebSocket application, beyond what we've just looked at, you'll probably need to tie this risk, resolved into a callback, some sort of function. That's going to do something with the data that might be coming back from the SQL server. And also we need to move that loop the do while into the periodic timer so that other things can happen in between those my SQLI hole calls. If we just keep pulling and not letting anything else in, then our server's not going to work. It's just going to be sitting there waiting. And anytime there's a query, the person who's trying to connect or send a message it's just not happening and it won't work well. So I've created a really simple, it's got two classes library that you can use whether you want to use it or not.

Doesn't matter to me, you won't hurt my feelings. If you say, oh, I don't like that at all. I don't want that. The reason I'm showing it to you is so that you can go look at the code. The basics of here's a query that clearly has callbacks in this particular one, I've given it a call back for a success and the separate callback for failure. So, you know, if the, if the query doesn't work, what should I do? You have these callbacks that you can pass things into. And then once the queries done it, we'll go ahead and run those functions that you've given it. So with this we have this connection manager so that John Kurt async, my SQL gives you a connection manager and it gives you a query object. So the connection manager, when you need to push that in, on that loop. Okay. So now I've shown you a little bit more information here in this bootstrap file. We've got the DB connection manager that we're instantiating, we're instantiating our app, we're instantiating the web socket server.

Then we're setting up the factory, which gives us the server. Okay. Now the web socket server was created with the app inside already. And the HTTP server that we're instantiating directly when we're creating the factory or calling the factory on the IO server that gets the web socket server passed in. So right in here, we're, we're passing that in again. But what this gives us is the ability to have both the loop and the web socket server available. So here's where I'm enabling keep alive. This is I'm setting it to 50 seconds that we have a heartbeat happening every 50 seconds, but then I'm setting this connection manager to run a hundred times a second. If it can't run that off in its way, it won't run that off. And it's really simple, but the periodic timer will make this happen up to a hundred times a second. When you put it in like this, and it's just going to call the DB connection managers reap any.

Now the reason that I've created this app separately and the web socket server, the web website service that we can have the keep alive down here, made the app separately so that we can pass in this connection manager that way inside of our router, we have access to the connection manager and we can push queries into it. And then the loop down here is going to be running to reap anything and allow that connection manager to check, to pull and then reap any queries that are ready and also run any novice functions that have been attached vomit, little side note, little side trip. Now let's talk about variable references and pointers. That's a lot of contention because references and pointers in PHP don't mean references and pointers. And see, I am using the word reference and the word pointer in its meaning in PHP.

So that being said, objects are one way res strings flows into jurors. They're stored another way. Okay. When you create an object, PHP puts that object in an object store and the variable that you're assigning it to is actually a pointer to that object in the object store. So when you copy you know, the variable that you've assigned that new object to you're actually copying the pointer, which points to the same object in the object store. So if you update that object from one place, it's going to see those changes in the other place as well. That's an important aspect. When we start looking at passing things around, how do I make sure that in one scope, when I'm sharing something in another scope, that those two things stay in sync objects is one way to do that. Anytime you put something in an object, like an SPL object storage then you have that ability to keep things the same everywhere.

When on the other hand, when you have an array or a string or a float if you copy that and let's say you have a variable, a string, a equals five, and then string B equals string B and are not going to track together. If you change B, it does not change a, if you change a, it does not change B. However we can, when we're doing that assignment from eight into B, we can pass it by reference passing by reference causes the second variable to track the first variable. So if we say string a equals five string B equals and string a. Now, if we change B a changes, if we change a beachy changes, another thing with references is that if I have something that's been passed by reference like that, and I unset one, it doesn't unset the other, it just removes the reference.

So if I have a equals seven B equals and a, and then I do an unset, a I still have B and it's still equal seven. Now, if you want to copy an object and make it a different object in the object store and have two different objects going, either need to create a new one, or you need to cologne it. So also just really briefly what I mean, because sometimes it's easier to understand code waste. For me, it's easier to understand code string, a equal seven string B equals string, a string equals three. What is B B is seven now string C equals ask by reference and string that and makes us pass it by reference. Now, if I set a two 22, B is still seven a is 22, obviously. And what is C C was passed by reference. So it's following a, so C is now 22, and this is how references work.

Now, the pointers and objects objects. One is new standard class object, two equals object. One object, one test equals test object to test is also test because object two is appointed to the same object in the object store. Now you can still use references with objects, and this is where it gets a little crazy. If you say object three equals a reference to object one, and then you say, object one is a new standard class. Now you have object one and object three, both pointing to the new standard class and object two is still pointing to the old standard class. So object two still has tests in its test property. But if we say object one test equals test two, and I'll find the object one. And onto three, both have test set to test two, because it's a reference. So they're fun, fun things, but when you start passing variables and objects around and PHP into each other scopes, you get this kind of mess.

And sometimes it's easier to just do everything by reference, but technically if you've done it with an object, you don't need to pass it by reference to keep the scopes in in many ways the same at this point, with the object one object to object three, just to push it a little bit further. We have tests to test and test to which goes back to those things. So these are not as functions that we're using to pass around here can modify variables that exist outside of their scope. If they are one passed in from the color to the function, by reference as a parameter. So the function that's calling that anonymous function. If it passes something in and you've defined the function with and parameter, then what comes in is actually going to be a reference to be scope of the variable that was in the color. And number two is if you're trying to track and modify variables that are in the definers scope, then we do that with the use language construct and you can pass things in through use. So we have a couple very specific things this, and when we define a function and string parameter, this parameter comes in from whoever calls the function and we can work with their scope. If we have this use, then we're, then we're dealing with the scope of where that anonymous function was created.

That was third way. You can modify things is obviously objects because they're pointers. And I think that one's a lot easier to understand, but we have pointers. And we have references. They all are in some ways similar to pointers outside of PHP, but none of them are actually pointers outside of PHP. It's very specific PHP, construct of winter, a PHP construct of a reference and how they manage those in the background is, is beyond the scope of what we're talking. Not today. We have this connection manager and we have this function. This is within our our controller. If you need to create an asynchronous query, you would make this new object, which is a query you would pass in what you need to do. And then you pass in an anonymous function. And that anonymous function can also take something from the scope where we're creating that function, that anonymous function has outside of its scope.

That con the connection. So if we pass that in with the use construct, and this object is available on the other side of the query, so the query can run select star from posts, which will probably take a long time. If you have a lot of posts and then on the other end of it, it can do something with that and that connection. So you can still manage to ask a database for something and then send it to your user. It's just really a different way of looking at things. So let's step back a little bit and see if we can make a brief little demo. So what I've done is created a basically following the same stuff that we'd be already have, I've created a server. And what it does is instead of working with my SQL connections, if we can see this what we have is a periodic timer that is going to get the value of Bitcoin and the value of ether.

What's the point of this? Let's say you have a data source that you need to get stuff from synchronously, and you have 10,000 climates. You don't want all of those clients getting it from the original source. You want it to come through this other place. You can have one thing that's hitting constantly and then be able to pass stuff out. So as you can see our servers running, it's getting ether. I've also got a thing there to check the memory and just keep track of how much memory is being used. And you see, it kind of goes up and down when it's higher. That's because it's actually also getting Bitcoin. And not just either, but both. And when it's still a little bit lower it's because it's just doing ether that very second, I've also added a timer that we'll go ahead and run the garbage collection.

And you'll see when that happens, that it drops them and read back down closer to the starting point. But what this ends up looking like is, you know, we have basic page that we can type the number into. So we have this this web socket sitting in the, in the JavaScript world that is connected to our server. And whenever the price of either Bitcoin or ether changes, that's when it sins. But if you're watching over here versus over here, sometimes when this happens, you know, we receive data over here and it immediately updates that other times you'll see that it got the ether and it didn't do anything over here when nothing happened. That's because the ether was still the same, the price didn't change. So the server checked, but it didn't find any differences. So it didn't bother sending it out. You can also see this heartbeat that you know, happening in here where every 50 seconds the server sending a pin, it's a single frame, very small, and it gets back a pong.

And that's it. This little thing will allow us to work with for example, a load balancer in AWS. It keeps it happy. So it doesn't just disconnect them. So this is how I've found to kind of get around that. Oh, and Bitcoin changed. So now this has been updated also. We have you know, if you send an invalid route, it fires back in error, route, not found if you send a valid one to the hello it actually is going to send back hello, and you can see that right here, but I haven't put it so that it pops up anywhere, but you can see it coming in, down below in the preview of what's actually happening in our web socket connection. So really simple, really straightforward, but this way you can start having your users. You don't have to pull your site to get the cost of your server.

Whenever there's a change in Bitcoin, however, it's getting it can send it itself directly to whoever's connected. The clients can receive that without having to do any polling, it's gonna save a lot of overhead and you get much closer to real time. Imagine if you're polling, you're checking every three seconds. That means the second, the data is always three seconds old. If you slow your poll down your poll, maybe every, let's say once a minute, that means people almost always have out of date up to a minute old numbers, which as you can see, they change quite often. So for something like the cost of ether or Bitcoin, this might be a really good solution to be able to give that real-time data out to your client without all of the overhead of polling.

So we've done all of this and looked at how you can create a a ratchet server. That's working with web sockets. Now let's talk a little bit about long running applications and how you keep them running. For example, how you keep things moving smoothly. So the author of rat, it actually recommends a couple of things. And I found these to be very helpful. One is a service called supervisor deeds, a supervisor that just watches on your server to make sure that a service is running. And this can be set up to work with PHP, and it can call PHP with your server script and it can start that service. And then let's say you do have a problem in your code and it throws an error and it dies. A supervisor D is actually watching you know, what's happening. So it can save stuff to logs for you...



Showing 1 to 1 of 1 comments.
ampersand - 5 years ago
Aren't objects passed by reference? at 47:42, why are you passing the $app with &?



PHP Tutorials and Videos