Require PHP 8.40:00
All right, so listen up, we're in a slightly awkward situation. As I record this video, it is December 2024, and this very week, in fact, PHP 8.4 was released. Now, as part of this release, we have access to both property hooks and asymmetric visibility, which somewhat changes how you'll go about structuring your classes. So all that said, keep in mind if you're going to work along with this video, it is assumed that you will have at least PHP 8.4 installed on your machine. Go ahead and check. Open your terminal and run php -v, and yeah, if it's 8.3 or less, you will need to upgrade in order to work along. Otherwise, if you're not quite ready for this, feel free to skip on to the next video.
Public Property Setup0:48
in order to work along. Otherwise, if you're not quite ready for this, feel free to skip on to the next video. All right, let's get started. So let's begin with the problem, and we'll discuss getters and setters, and then ultimately, property hooks. Now, imagine you have a User class, and within the constructor, just for brevity and demo's sake, all you have to provide is an email address, and that'll be a string. All right, now traditionally in php, of course, we'd assign this to a property, right? So we do something like this, and then I create the property, and just for now, I will declare it with public visibility.
So we do something like this, and then I create the property, and just for now, I will declare it with public visibility. So public string email, and this looks good. So if we give this a shot, I will instantiate User. I will provide my real email address, and yeah, of course, if I try to echo the email property, if we did everything correct, we should get jeffrey@laracast.com, and we do. This is great. All right, a couple things, though. If we want, we could graduate this to constructor property promotion, and that just means we
All right, a couple things, though. If we want, we could graduate this to constructor property promotion, and that just means we could assign it as part of the parameter list like this, public string $email, and now that will assign it and create the property in the process. So we could do something like this, and we'll still get the exact same end result. Give it another run, and yeah, I still get my email address. All right, good to know, but nothing overly new here at the time of this recording. What else can we do? Well, is there anything preventing me from changing this $email to something that is, in fact, not an email address?
Well, is there anything preventing me from changing this email to something that is, in fact, not an email address? And the answer is, well, no, not really. So if we did something like this, $userEmail = array, well, we do have some affordances here. So if I give it a run, I'll get a fatal error because we did declare that email should be of type string. But now we're trying to assign an array, and that triggers an error, which is cool. That's helpful. But yeah, I could still do something like this, right?
That's helpful. But yeah, I could still do something like this, right? I could just assign it to gibberish, and it's going to work. So if I give it a run, now $email has been set to something that is not an email address. So now it's inconsistent, right? And we don't want to allow for this, at least in this example. So it's almost like we need a way to hook into this process. We need a way to say, well, when you assign the email address, I want to validate that what you give us is, in fact, a real email address. The only problem is, again, traditionally, in PHP 8.3 and below, there was no way to
Getters and Setters3:19
what you give us is, in fact, a real email address. The only problem is, again, traditionally, in PHP 8.3 and below, there was no way to do this. So instead, we created getters and setters. Here's what we do. First up, we would declare the property itself protected or private. Remember, private means this property is private to me alone, to this class only. protected means it's private to me and any child classes that extend from me, me and my children. For now, I'll stick with private.
my children. For now, I'll stick with private. All right. So now we've solved one piece of the puzzle. You can no longer directly access that email and change it to anything you want. Cool. And in fact, we can see some squiggly lines here that prohibit it. So now I can create dedicated methods that are responsible for fetching the email and setting the email. Let's do that now.
setting the email. Let's do that now. I'll create one to get the email, getEmail. This is a common convention. And yeah, that's just going to return the value of this property. Then I'll do another one, setEmail. And yeah, ultimately, this will assign the email you give us to the property like this. So now we've effectively reproduced what we had before, right? If I update this, I could say $user->setEmail(), and then I could say echo $user->getEmail(). So yeah, you can see right now we haven't really solved the gibberish problem.
If I update this, I could say user set email, and then I could say echo user get email. So yeah, you can see right now we haven't really solved the gibberish problem. We've just switched to using getters and setters, but one step at a time. So I give it a run and yeah, it is still working. Okay. So now think about it. Within this method, I have my hook of sorts. I can intercept the process where we assign to the email property and perform any kind of validation that would make sense. Maybe something like this.
Add Email Validation5:11
of validation that would make sense. Maybe something like this. If not, filter $var and we'll pass the $emailAddress and our preferred filter, which in this case would be filter_var with FILTER_VALIDATE_EMAIL. Yeah. If it is not valid, then why don't we throw a new InvalidArgumentException? Email must be valid. All right. Otherwise, assuming that it is valid, then we assign it to the property. All right.
Otherwise, assuming that it is valid, then we assign it to the property. All right. And this is what we want. We have our hook. We intercepted the process of assigning to the property and we performed whatever guard clause or validation that was required. So now this code should trigger an InvalidArgumentException. We give it a run and sure enough, we get InvalidArgumentException. This is what we want. If we now update it to something that is an email address, it should work properly.
This is what we want. If we now update it to something that is an email address, it should work properly. And it does. And yet this is the entire motivation behind getters and setters. Think about it. The moment we changed the email property from public to private, we removed access to it from the outside world. We could no longer say $user->email. Instead, we had to create a dedicated getter method that so often just returns the value of the property.
Property Hooks Migration6:20
Instead, we had to create a dedicated getter method that so often just returns the value of the property. But still, it can be helpful. All right. So now let's move on to PHP 8.4. 8.4 introduced what's known as property hooks. And really, property hooks remove the need for getters and setters completely. So you could just migrate from your getters and setters implementation directly to property hooks if you want. And I'll show you how to do it.
hooks if you want. And I'll show you how to do it. First up. All right. So I'm going to start right up here by changing the visibility back to public. Remember, the only reason we made it private was so that you couldn't directly access this property. And that's because we needed to perform some validation before we assign to the property. But now we can solve that through hooks. So it can once again return to public.
But now we can solve that through hooks. So it can once again return to public. All right. Next, I'm going to command click or option click here, and I'm going to replace it with traditional property declaration, just like I showed at the beginning of the video. All right. So now when I define the property, I'm going to add braces here. And this is where I can set how we should handle getting the email and how we should handle setting the email address. Now, again, keep in mind if you're working along and right now your editor is blowing
handle setting the email address. Now, again, keep in mind if you're working along and right now your editor is blowing up with a bunch of squiggly lines, it's probably because you haven't turned on at least PHP 8.4 highlighting support. So make sure you check that and then those lines should go away. All right. So, yeah, let's just do this. Let's take our getter logic, which just returns the value of email, and I'll paste it in. Now I can get rid of that getter entirely. All right.
Now I can get rid of that getter entirely. All right. Next, let's do the exact same thing for my setter. So I will cut that and move it up to our setter hook. Now I can get rid of that. But you'll see right here my editor is squawking once again because it doesn't know what email is in this case. Remember before we passed through the email, we accepted it as an argument. But now how do we handle that in this case? And the answer is we can do it in two ways.
But now how do we handle that in this case? And the answer is we can do it in two ways. First up, I could explicitly accept an email like this string email. And now that will work. However, there's also a shorthand. I'm sorry, there's a shorthand if we want. I could remove this entirely and instead use a magical of sorts, a magical value variable name. So I would update this like so. Just remember value is going to refer to whatever you assign email to. So if I say $userEmail equals foo@example.com, value will now be foo@example.com.
Just remember value is going to refer to whatever you assign userEmail to. So if I say userEmail equals foo@example.com, value will now be foo@example.com. All right. But that's it. We've now migrated to dedicated property hooks. And here's the cool thing. I no longer have to reach for these kind of awkward and clunky setThing method or getThing method. Instead, I can interact with the property just like I would naturally want to. So let's give it a shot.
Instead, I can interact with the property just like I would naturally want to. So let's give it a shot. Let's echo $userEmail and we give it a run and we get my email. Now let's assign it $userEmail equals changed@example.com and we give it a run. And now that worked. But yeah, remember the original motivation was disallowing things like this where you give us gibberish, a non-valid email address. Now if we run it, there we go. We still get our InvalidArgumentException. But yeah, now we are handling it within a property hook rather than a setter method.
We still get our InvalidArgumentException. But yeah, now we are handling it within a property hook rather than a setter method. Now keep in mind, sometimes maybe these hooks can get a little lengthy and there's nothing prohibiting you from deferring to an outside class or just calling another method within the object. All right. So a couple more things to be aware of. First up, sometimes for your getter logic or even your setter logic, you'll just reach for like a one line expression. So maybe when you set the value, you want to ensure that it's always lowercase or when
for like a one line expression. So maybe when you set the value, you want to ensure that it's always lowercase or when you get the value, it should always be lowercase or something like that. In those cases, if you want, you can swap this over to the arrow syntax. Change it to an arrow and now you could do something just like this. And notice these are interchangeable. I could use the arrow syntax here and dedicated braces, the long form down here. It doesn't matter. It's all going to work just the same. Next up, one thing I really like is we can effectively enhance these properties when
It's all going to work just the same. Next up, one thing I really like is we can effectively enhance these properties when and if we need to on demand. So for example, in this case, it just returns the value. That's not overly useful to me. So I can remove this entirely and it's still going to work. We give it a run and we get our exception. But if I get rid of that entirely, we can access the email address. So now this works fine. But if at some point in the future, we need to change what happens as part of fetching
So now this works fine. But if at some point in the future, we need to change what happens as part of fetching an email, I can now do that. And I could do something like, I don't know, string replace and look for the @ symbol and replace it with at. Maybe we're trying to block some kind of email address scanners or something like that. I don't even know if that still works these days. But yeah, if you wanted to do something like this, you could. So now when I access the email, it'll say Jeffrey ( at Laracast.com ). And yeah, again, the point being you have the ability to hook in here, which is really
So now when I access the email, it'll say Jeffrey at laracast.com. And yeah, again, the point being you have the ability to hook in here, which is really good. Next up, what else? Maybe one final thing. There might be situations where you effectively want the email address to be immutable. So in that case, you would never have a setter here. But the only problem is because I returned this property to public, I can do that, right? I could once again say $user->email equals gibberish. And if I echo the email, we're right back in that same position that we were at the
Asymmetric Visibility Control12:08
I could once again say $userEmail equals gibberish. And if I echo the $email, we're right back in that same position that we were at the start of the video. So here's another new thing in PHP 8.4. We have access to, and this is a fancy term, red warning here, asymmetric visibility. All right, so asymmetric means not the same, right? Symmetric would mean if for public, then we can publicly access the $email and we can publicly set the $email. But if we have asymmetry, that means accessing visibility will be unique compared to setting visibility.
But if we have asymmetry, that means accessing visibility will be unique compared to setting visibility. All right, so yeah, all of this is just a way to say you can control the visibility for accessing a property and also control the visibility for setting the property. And those two values do not have to be the same. All right, so we could do something like this. I could say, all right, you can access this email, but you can never assign to it. So I will update this and say private for setting. And yeah, this syntax will take a little bit of getting used to, but it'll be fine. All right, so now notice immediately when I add that, my editor informs me, hey, you're
And yeah, this syntax will take a little bit of getting used to, but it'll be fine. All right, so now notice immediately when I add that, my editor informs me, hey, you're trying to assign to email and that is not allowed. All right, next up, notice that the public keyword here is kind of faded. And that's just a little warning that if you like, this is somewhat superfluous. Because this is so often the case, we can remove it. And it is now assumed that email has public visibility for accessing the value, but private visibility for setting the value. So do whatever you want. If you like to be explicit, then include them both.
So do whatever you want. If you like to be explicit, then include them both. Of course, if this should be protected instead of private, that's fine. Or if you prefer to omit that and keep it a little shorter, that would be fine as well. But yeah, now we have a User class that has an email property that can be read, but cannot be set from the outside world. OK, that would mean any situation where you assign to the email would once again have to go through some kind of dedicated method call. So something like this. And then you could once again create your method, updateEmail, and then you could assign
So something like this. And then you could once again create your method, updateEmail, and then you could assign to it like so in a more traditional way. So yeah, notice if we give it a run, that works. You can once again add your filter logic. You can handle this however you want to. And that's the entire point. OK, so yeah, a lot of this stuff is new, even to me. Once again, you have access to property hooks and asymmetric visibility in PHP 8.4. Yeah, once again, if you're on 8.3 or lower, that's OK.
Once again, you have access to property hooks and asymmetric visibility in PHP 8.4. Yeah, once again, if you're on 8.3 or lower, that's OK. You can skip over this if you want or if you're very curious. And if you're an early adopter, download the latest version of PHP and give it a shot. Otherwise, in the next video, we'll go back to more widespread object-oriented training. I'll see you then. Bye.
