Playing
01. Custom session handlers

Episodes

0%
Your progress
  • Total: 20m
  • Played: 0m
  • Remaining: 20m
Join or sign in to track your progress

Transcript

00:00
So before we start coding this up, we're going to just take a look at how this all fits together and how it works.
00:06
So you can see here that what I am doing is actually choosing the handler for my sessions. And this is the class that we are going to be creating to store and read data from our database in terms of sessions. Of course, I'm going ahead and starting sessions and I'm just setting here a value into a key
00:25
of name. So if we just go and echo this, we'll be able to see that this is actually working. So we'll just echo session name. If we head over to the browser and give this a refresh, you can see that sure enough, once
00:36
we have stored that, we get it back. Now what's interesting is over in my database that was previously empty, I have this sessions table which currently looks empty. When I go ahead and give this a refresh, you can see that in fact it is actually storing
00:52
this data in here. So we have a unique session identifier which you'll be able to find if you head over into your development tools over in the resources tab under cookies. You'll see that just there.
01:04
So that's what that relates to. And of course, what we're doing is we are writing to and we are reading from the database. We also have access which is when we last wrote the session. And then of course, we have the data that's stored alongside that session as well.
01:19
So really, not many columns in here to actually write to. Pretty straightforward. So now that we know how it all works, we can go ahead and start to build our database session handler.
01:31
But first of all, let's just jump over and take a look at the actual database schema. So the database schema then is pretty straightforward. We have three columns as we've already seen. The first one will need to be a varchar just because it's storing, you know, not only integers
01:49
but it's storing characters as well. And this is our primary key of our table and I've given this a length of 255. Now access here is an integer. This is just a timestamp.
02:00
You can really store this however you want. But when you're storing an actual Unix timestamp, it generally makes it a little bit easier to work with. And you don't have to have a length of 255 here.
02:10
This was just set as default. But of course, don't make this too short or you'll cut your timestamp off. And then finally, we have a data column. This is just a text type.
02:20
We want to store lots of information in here potentially. So we want this to work, therefore we give it the type of text. So that is pretty much it. If you're following along and building this, you want a sessions table with these columns
02:33
and then we can go and get on with building the actual class. So let's bring up my text editor. We're just going to be doing this in a single file. But of course, because we are creating a class, we want to put this somewhere sensible and
02:45
either require it in or auto loading in if you are doing that. So before we even start to touch this, we need to kind of figure out what methods we need when we hook this onto our handler. So if we head over to the PHP manual over to session handler interface, this will give
03:04
us the clue as to what we need to implement. And in fact, we will be implementing this interface and it will force us to implement these six methods. So we have a close method.
03:15
We have a destroy method, GC, which stands for garbage collection, open, read and write. So open and close are responsible for checking your database connection is open and close is responsible for checking if it's closed, not just your database. This can be anything.
03:33
So you could be technically writing to local file storage, but these are two methods that handle that kind of thing. So whether something's open for writing and reading and whether something is closed. The next one is destroy.
03:47
This is either when we destroy a single session or we go ahead and destroy all sessions. We need a handler to be able to go ahead and delete this data from our database. And we also have open, read and write. Oh, sorry, we have read and write.
04:03
So read and write are pretty much responsible for reading session data by a session ID and write is responsible for taking a session ID and storing data alongside that ID. And of course, we have garbage collection as well, which is part of PHP. It will just allow us or allow PHP at least to clear up sessions that we no longer need.
04:25
So this happens already, but in our case, we need to implement our own method to handle garbage collection based on the maximum lifetime of a session. So we'll talk about that. That's probably the most complicated method here.
04:38
The rest are pretty easy to implement. So considering this is a standard PHP library interface, what we can do is go ahead and start to build our class and implement this. So to get started, then all we want to do is connect to our database.
04:54
So let's do this in a try catch, because as we know, if we don't have a database connection available, we can't really do much. And of course, we can't do anything with sessions. So let's use PDO here.
05:05
I'm using MySQL, but of course, you can be using anything. And we just go ahead and enter our host information, so local in my case, and the database name as well. Mine happens to be project.
05:16
And go ahead and enter your credentials as well. Mine are homestead and secret. So once we catch a PDO exception, or if we do catch a PDO exception, that means we can't connect.
05:30
So I'm just going to die here and say failed. And of course, you'll want to do something sensible, maybe log something or display a nice message. So the class that we're going to be creating then is going to be called DatabaseSessionHandler.
05:45
The reason I'm calling it DatabaseSessionHandler is because in future, if you choose to use, say, memcache or another cache solution to store session information, you want to call this something like memcacheSessionHandler or something else. So it's very important that we explicitly kind of give this class a name as to what
06:03
it's actually doing. And here we can just implement that standard PHP library interface. So we just implement that, and we are done. So now this will force us to implement these methods.
06:15
We can test this out by using the SessionSetSaveHandler method, or function rather. So you can see here that there's a couple of ways that we can do that. We can define each of these on their own, or we can pass through a class to this, a new instance of a class.
06:30
So in our case, it's DatabaseSessionHandler. So let's just go and run this on the browser and just see what happens here. So you can see it contains six abstract methods and therefore must be declared abstract or implement the remaining methods, which is just a complicated way of saying you need
06:48
to create these methods. So we need to go and fill these in now. So we'll fill them in kind of like a skeleton structure, and then we will implement them afterwards. So we have read.
07:02
So this is responsible, like I said, from taking an ID and grabbing that information about that session. So essentially just pulling back the data in this column. And next we have write.
07:16
So this will obviously write to our session. And for this, we need to take a ID in and data. And by the way, we're not actually going to be passing in this ID and data. The SessionSuperGlobal will do this for us because we're attaching this and calling it
07:32
or giving it our SessionHandler. So we're telling PHP that this is our SessionHandler. It will do all the hard work for us. So then we have open.
07:41
Now essentially this, like I said, is just to make sure that your database connection is up, or if you're using, say, file system for this, maybe storing it locally, you'll need path as well and name. So we don't need this information here because in this method, all we're going to be doing
07:57
is checking that we have an active and open database connection. And then we have the opposite, which is close. This just makes sure that the connection is actually closed, just so we know. And then we have destroy.
08:13
So let's create this here. And of course, like I said, this is responsible for destroying sessions. And lastly, we have our garbage collection, which is shortened to GC, and we have our maximum lifetime passed into this.
08:32
So before we go any further, let's just start up sessions down here. We're going to do this under where we've set our handler. And let's go and try and set a session at least. We'll see an error here.
08:43
So let's say name equals Alex. And let's see what happens over on our browser. So we get a couple of errors. And the reason for this is obviously we've not filled in our data, filled in our methods.
08:55
But of course, we have things like session callback expects true or false. Now there's going to be certain instances where within these, we're going to need to confirm that something was successful. So for example, when we write to our database, we need to know, well, did it actually work?
09:11
If it didn't work, PHP will handle that for you. Otherwise, we can just say, yes, it did work. And everything should just work as normal. So that's the kind of errors we'll get.
09:20
If you see them along the way when you're following along, then that's what's happening. So I guess we could look at open and close first of all. This probably makes sense because we can't really do much without them. These will be called anyway.
09:35
So to check if a connection is open then, well, we actually need a database connection in here as a dependency first of all. That makes total sense. So what we're going to do is when we actually set this, we're going to pass in our PDO database
09:48
instance and then we can just accept it within this class. So we create a constructor for this. And then obviously into this, we receive DB. And we can type hint it to PDO to make sure that we are actually receiving that in.
10:03
And then we can just set it to a property. So let's do this now. Okay. And we'll just create a property up here.
10:11
Perfect. So now under open and close, we can check if that is an active database connection. For this, we can just say this DB and then we can return true if that's the case. Now as a default, we want to return false.
10:27
That makes a lot more sense. And to close a PDO connection, we just say this DB and we basically just set it to null. That's how we essentially close a connection. And then here, we just want to check if that's the case.
10:41
So we can just say equals null. We don't need to do this because from the following line, we know this is true. But it kind of makes sense just to keep it in there as a reference point, if anything, and just so it's a little bit clearer what we're actually trying to do in here.
10:56
So now that we've done that, we can focus on write and then we can actually start to see some evidence of some data stored inside of the database. So to write a session, then we have ID and data. So all we really need to do is create some kind of PDO statement or a prepared statement
11:15
at least. And it will be this DB prepare. We'll go ahead and enter into here our query. And in this case, we're going to say replace into.
11:24
Now this just means that when we insert again, we're just basically replacing the previous data. So that's really important. And the values are ID, we're going to pass in a timestamp, which is also really important.
11:38
And we're going to pass in that data. So these are just placeholders, we can go ahead and execute the query down here. So we're going to say something like insert, and we're going to say statement, execute, this will run the query with the data that we provide.
11:53
And that data is the ID that we get back within this method, we have a timestamp because we're just using a Unix timestamp, we can use the PHP time function, but feel free to do this however you want, if you're wanting to do it a little bit differently. And we just have the data in there simple.
12:10
And the reason that we assigned the execution of this statement insert is so we can check if it was successful, PDO will return false if this fails. So in this case, we can just say if insert, and then we can return true here. Otherwise we can go and return false.
12:30
So pretty straightforward. So we can actually test this out now, because we don't need any other methods to actually make this work. So let's just run this page.
12:40
And it looks like we've got an error. So let's just double check that we've done everything correctly here. And just make sure everything here is okay. And there we go.
12:51
So I misspelled sessions to session, so we had a little error. So now when I refresh, what we would expect to see after we've done that is some data in here. We have the session ID, we have the last access in Unix timestamp, and we have the data stored
13:10
alongside of this. So you can play around with this and add more data, and you can kind of see the structure of this and how it looks. But essentially that is it.
13:17
We've already started writing stuff to the session. Now we want to look at reading. So if we go up to the read method, this is pretty much just the opposite. We have an ID.
13:28
We can create a prepared statement here, just to say this DB prepare. We can go ahead and pass in our query. And in this case, it will be selecting from sessions. And of course, we want to only select data from sessions.
13:46
And that is where the ID equals the ID that we get passed into this method. And then we can just execute it, like so. The reason we're not assigning this to a variable to check if it worked is we'll see in just a moment.
14:00
So what we can do now is if this fails for any reason, we just want to return an empty string. We don't want to return false. So to do this quickly within an if statement, we can just say if row equals.
14:12
So we're doing an assignment and a check at the same time. We can say statement, fetch, using the fetch method. And we can pull this back as an object. You can put it back as an array if you want.
14:23
And if that's the case, that means that the query was successful. We now have that data. So we can just say row, which now represents that row on that ID. And then just the data property, which will literally just be what's in here.
14:36
Okay. So if that fails for any reason, we can just return an empty string. And we're done. So what we can now do is echo session name.
14:48
And to test this properly, we're actually going to comment this line out. So we know that this is definitely being pulled back from the database. So if we just go and refresh here, you can see that indeed, we are getting the value Alex.
14:59
And we can go over and change this just to double check it's working. Of course, I wouldn't recommend you go ahead and do this, at least in production. But there we go. If we change that and refresh, and this is interesting.
15:12
So I think the problem here is that we have a length here or a size at least. And we've got 1, 2, 3, 4, 5 now rather than 4, which was for Alex. So I think if I change this to 5, we should be good and see Billy. Yeah, we do.
15:28
So yeah, again, a good reason in production not to tamper with sessions. But we can see that this is definitely reading from the database now. So let's put this one back. And we will start to look now at destroy.
15:43
So destroying then is obviously when we destroy sessions in whichever way. So this is pretty straightforward. We get an ID. What do we need to do?
15:51
Just delete that row from the database. So we can just say statement and this DB prepare. Go ahead and enter our query, which is literally just delete, not select, from sessions where the ID equals the ID that we give.
16:09
And then we just go and assign this to delete. Because we need to check that this deletion was in fact successful. We just execute this, passing in the ID like so. And then we just check if it was deleted or not.
16:24
So we can just say if delete, then return true. And otherwise, we want to return false. So I think this works for when we do something like session destroy. So let's just test this.
16:42
That should at least go through and delete all of the items in our session by their ID. And we should see that that's deleted. Perfect. So let's just bring back what we had.
16:52
We don't really need to because the next thing that we're going to be focusing on is garbage collection, which is pretty difficult to test. But what I'm going to do is just explain to you as we're going along exactly what this does and generally when it's run.
17:06
So a little bit of background about garbage collection. You essentially have within your PHP configuration the probability that the garbage collection is going to be run. And all this really does is it will check based on the maximum length of a session whether
17:22
there are any sessions either in your file system or in your database that are considered old based on your settings. So in this case, what we want to do is do something like a limit. And that's going to be the current time minus the max that's passed in.
17:40
And then all we want to do is because we're working with a Unix timestamp, this is really easy because we can very easily do arithmetic with Unix timestamps. So we can just check that that access column just here is less than the limit that we have. And then if that's the case, we can delete it.
18:01
So of course, with SQL, this is really straightforward. We just say this db prepare, of course. And again, you don't really need to do prepare statements here necessarily. Probably for all of these actually, it's not a massive concern.
18:15
And particularly for this one because all we are doing is passing in trusted data. But I think it's just generally a good habit to get into. So I don't consider there being anything wrong with doing this at all. We won't really get any significant speed implication here.
18:33
So what I'm doing is I'm just deleting from our sessions where the access timestamp is less than the limit. And what we need to do here is go ahead and assign this so we can check that it was the case that it was cleared by garbage collection.
18:48
And here we will just execute this as we've seen before. So let's pass in that limit here like so. And we can double check this. So we can just say, well, was that deleted?
19:01
Well, yes, we want to return true here. And then otherwise, we want to go and return false. So grab the limit based on the maximum life of a session and create a prepared statement deleting that.
19:17
Execute the statement. Check if it worked. So essentially, and it's more likely when your user goes away from your site, maybe for, you know, a day or so or if they haven't been on your site actively bumping up the
19:33
time on their access timestamp here, because remember, when we're writing to sessions and doing them operations, we are updating that timestamp. So it's always going to be kind of ahead of it. And it won't ever be deleted unless the user hasn't been around for a while.
19:48
So that's essentially what we're doing. So with that done, we've pretty much done all of the methods that we need, some more complicated than others, but really pretty straightforward stuff. And what we can now do is work with sessions.
20:04
So I've just stored a value again with Alex. We can go ahead and overwrite that if we want to at any time. And of course, what will happen is this will update. If we just go ahead and refresh, check our database, you can see that that's updated.
20:19
The access was just bumped up there like I was just speaking about. And that is pretty much it. So if you are going to use this, and it's a good idea just to have this in place anyway, go ahead and put this class into another file.
20:32
Obviously, you'll be connecting to your database. Set your session handler above when you do session start, and then just use sessions as normal. And you'll have all of the benefits that we spoke about in the first part of this miniseries.
1 episode 20 mins

Overview

How to switch out your standard PHP session handler and use the database instead.

Alex Garrett-Smith
Alex Garrett-Smith
Hey, I'm the founder of Codecourse!

Episode discussion

No comments, yet. Be the first!