تماشای این درس نیاز به اشتراک حرفه‌ای دارد.

Playlist and type pitfalls0:00

Alright, next up, I'd like to discuss data transfer objects and general php types. So first step, DTOs, as they're called. I'll warn you, it sounds complex and confusing and fancy, but it's really not. It's pretty easy to understand. Have a look. So once again, I have a Playlist class, and I can see here that we instantiate it with a name and a list of songs. So let's give it a shot. New Playlist. How about 90s movie soundtracks, maybe?

New playlist. How about 90s movie soundtracks, maybe? And yeah, add a list of whatever you were listening to back then. And I already know. You were listening to My Heart Will Go On by Celine Dion from Tightenic. Don't act like you weren't. I was there. I saw you doing it. Okay. So what I can do is, yeah, if I save this to playlist, and we want to access that song,

Okay. So what I can do is, yeah, if I save this to playlist, and we want to access that song, well, we know this property is set to public, so it's publicly available to the outside world. That's a good way to think of it. So I could say playlist, give me the songs array, and give me the first one, and why don't we just echo it to the console? Let's give it a run. php on the current file, and yes, I get My Heart Will Go On. Great.

PHP on the current file, and yes, I get My Heart Will Go On. Great. But here's the thing. Things don't always go the way you expect. Sometimes you think you're passing an array for songs, but it's actually null. This happens all the time. We always run into it. For example, maybe you are dynamically fetching songs from a database, or you're building it up from an API call. It doesn't really matter.

it up from an API call. It doesn't really matter. Even though you thought you had this array, ultimately what you ended up with was false or null or something like that. Let's do false. Well, now think about it. We are instantiating playlist, and we thought we provided a valid array, and now we're trying to interact with that array, and it's just not going to work. Let's give it a shot. We run it, and we get a warning.

Adding property type hints2:00

Let's give it a shot. We run it, and we get a warning. Hey, you're trying to access an array offset, so you're trying to access an item within this array, but it's false, and you can't do that, bro. So what do we do in these situations? We have a couple of choices. One, be very, very, very careful, or two, you could add types to your properties like this. If I come up to my songs parameter here, I'm going to add array. So now I'm saying I expect a songs argument from you.

If I come up to my songs parameter here, I'm going to add array. So now I'm saying I expect a songs argument from you. That argument should be a type array, and we're going to give it public visibility. Okay, so now if I don't change anything else, and we give it another run, php once again on the current file, now we get a fatal error, and this is what we want. Argument two must be a type array, but you gave us false, so we're just going to blow up because you didn't do what we expected, and I promise you this is going to avoid so many issues that you might run into along the way, and that's great. Also, we get some editor support. If I hover over this, it's going to let you know, hmm, we expected an array, but you gave.

Introducing DTOs for songs3:03

Also, we get some editor support. If I hover over this, it's going to let you know, hmm, we expected an array, but you gave us false. This is something you should take a look at, which is really good. All right, very cool. So let's bring this back to what we had before, and now let's move on to DTOs, or data transfer objects, or really whatever you want to call them. It doesn't matter. Think about it. It might be useful when I access this song if there was additional data or information.

Think about it. It might be useful when I access this song if there was additional data or information associated with it, like who is the artist who created this song? What album was it on? What year was it released? How long is it? I can't do that right now, right? All I get is the name of the song, and that's it. All right, so why don't you hit pause and ask yourself, what should we do in this case? How would you fix this problem?

All right, so why don't you hit pause and ask yourself, what should we do in this case? How would you fix this problem? All right, hit pause and have a think. All right, did you do it? Of course not. I always say I do it, and nobody ever does it, but that's okay. So let's go over it together. One option might be to use an associative array. So I could replace this with an array itself, and now I could say, well, the name of this song is My Heart Will Go On, and the artist is Celine Dion.

So I could replace this with an array itself, and now I could say, well, the name of this song is My Heart Will Go On, and the artist is Celine Dion. And the year it was released, I can't remember when Tightenic came out. Was it like 96? Something like that. And that would work, and you could rinse and repeat for all of your songs, and that would solve the problem, but it doesn't allow for much enforcement. So for example, what if the second item, for whatever reason, omits the artist? Well, now you could be in that same situation again, where you expect to have an artist key within the song array, but it's not there, and it triggers an error.

Creating a Song class4:37

Well, now you could be in that same situation again, where you expect to have an artist key within the song array, but it's not there, and it triggers an error. And it also just ends up being a little, I don't know, a little bit brittle, a little bit primitive. I think there's a better option. What if instead of an array, we graduated it to its own class called Song? All right, let's give it a shot. So I'm going to create a new class here, and of course, once we get a little further down the line, each of these classes would go within its own file. All right, we're just gripping it here for simplicity.

the line, each of these classes would go within its own file. All right, we're just gripping it here for simplicity. All right, so let's say, just for brevity's sake, that we only care about the name of the song and the artist, and we're going to pass those through the constructor. All right, we're going to have a string for the name, and then another string for the artist itself. And you know what? Let's also set the playlist name to be of type string as well. OK, so this is looking fairly good. Now I can instantiate our playlist, but check this out.

OK, so this is looking fairly good. Now I can instantiate our playlist, but check this out. Rather than giving it an array of strings, I'm going to give it an array of songs. And already, as I speak it out loud, it just sounds better. Give me a new song. My heart will go on by Celine Dion, and this is going to work great. All right, so think about it. This is pretty cool. Now let's do this. Let's var_dump playlist songs and take a look at what you see here.

Now let's do this. Let's var_dump playlist songs and take a look at what you see here. Let's run it. And now, yeah, we have an array of song objects. So if I wanted to grab the first song within that array, now I have access to an instance of this class, at which point I can access any data off of it. Or if I did eventually want to add behavior to that class, I could do so. Like maybe, I don't know, depending on what you're building, maybe you do have the ability to start the song. So you could add a method called start, if that would make sense.

to start the song. So you could add a method called start, if that would make sense. But for now, we're just assuming that this Song class is just kind of a wrapper for a clump of data, and that's why we often call it a data transfer object. It's an object full of data that we use to pass around wherever it needs to go. Cool. So take a look at this. If I access the name, let's give it a shot. Run it, and we get My Heart Will Go On. Let's try to access the artist off of that song.

Enforcing arrays of objects7:01

Run it, and we get My Heart Will Go On. Let's try to access the artist off of that song. And yeah, you can see how useful this is. Very, very cool. All right. Now, here's something to think about, coming back to the issue of types. If I go up to my playlist, yes, it is true we are expecting an array, and that's good. But what I'm really expecting is an array of songs now, not just an array. So for example, is there anything preventing us from accidentally passing false, and then a string here, and then null there?

So for example, is there anything preventing us from accidentally passing false, and then a string here, and then null there? Yeah. Are there any issues with that? And why don't we just say this? For each playlist songs as song, why don't we echo the song's artist or something like that? Well, of course, we know this isn't going to work. So I give this a run, and we get a bunch of warnings here. Hey, you're trying to access artist on null. And in this case, you're trying to access artist on string, and then artist on false.

Hey, you're trying to access artist on null. And in this case, you're trying to access artist on string, and then artist on false. Clearly, we are introducing so many bugs by not enforcing the consistency of this class as well as we possibly can. All right. So what can we do to fix this? Well, I'll warn you. It would be cool if there was a way to say, I expect an array of song objects. But at the time of this recording, which is November 2024, by the way, PHP itself doesn't have proper native support for generics and things like this.

Static analysis with PHPStan/Psalm8:23

But at the time of this recording, which is November 2024, by the way, php itself doesn't have proper native support for generics and things like this. And that's the term we might use. But we do have static analyzers that do provide support. And some of those are called PHPStan, Psalm, and they basically analyze your code to detect any potential issues that you might run into. It's very useful. But it's also a little beyond the scope of this course, and maybe a little beyond the scope of your current level, depending upon how long you've been coding.

and maybe a little beyond the scope of your current level, depending upon how long you've been coding. Nonetheless, if you feel like it, you might research it a bit. But just rest assured, we're not going to cover it too much in this course. It's kind of an extra credit thing that you might consider. However, if you did install SOM, you could do something like this. You could clear this and run it on your project, or in this case, my single file. Yeah, so notice it inspected this file, and it came up with three errors or issues that we might want to review. So real quick, if we have a look, mixed assignment,

and it came up with three errors or issues that we might want to review. So real quick, if we have a look, mixed assignment, unable to determine the type that song is being assigned to. Yep. So we're iterating over it, and it's just not sure what that should be, because it's inconsistent. If we come down here, mixed argument, same thing. So we're trying to access song.artist, but you can't do that off of a string or false or null. So it's very good about spotting potential bugs in your code base, which is cool.

but you can't do that off of a string or false or null. So it's very good about spotting potential bugs in your code base, which is cool. So now, and by the way, it'll even spot unsafe function calls and things like that. So if I had var_dump here, it's fine, obviously, when you're testing. But in a production environment, it could be a big problem that you should avoid, and it will spot that for you. Or I'm sorry, where is it? Yeah, right here. Unsafe var_dump. Cool.

Unsafe var_dump. Cool. All right. So yeah, think about it. Now, we have a way to say when I instantiate Playlist, and I pass through an array of songs, I expect each item within that array to be a Song object and only a Song object. But yeah, just as a reminder, php itself is not enforcing that. Your static analyzer is detecting it. And of course, you can integrate that into your editor.

Your static analyzer is detecting it. And of course, you can integrate that into your editor. Okay, so let's do this. At the very top, I'm going to add a doc block. And I'm going to say right here, param $songs is an array, but not just an array, it's an array of songs. And we can represent this like so. Okay, so now let's run it again. clear, run SOM. And let's have a look.

Clear, run SOM. And let's have a look. There we go. Invalid argument. This is the one we want. Take a look. Argument to a playlist, which is the songs parameter. It expected an array of songs, but we gave it a list of song, and then false, and then a string, and then null. And that's not going to do the trick.

and then false, and then a string, and then null. And that's not going to do the trick. All right, so it is working. And yeah, don't forget, if you want, you can integrate things like this into your code editor, very likely. So in my case, I could say, look for SOM, turn on the plugin here, give it just a minute. And there we go. Now I see our warnings pop up. So we have our potentially unsafe use of var_dump,

Now I see our warnings pop up. So we have our potentially unsafe use of var_dump, as well as that invalid argument exception, which we declared right here. All right, so we fixed that bug by ensuring that we only pass an array of Song objects to Playlist, and now it goes away. And if I save, you can see that warning has now disappeared. All right, so again, we're covering some slightly more complex tooling and ideas here.

All right, so again, we're covering some slightly more complex tooling and ideas here. But I think it's going to take you a really long way. All right, so in the next episode, I'd like to move on to composition. I'll see you then.

دوست دارید گاهی خبرهای Laracasts را ایمیل کنیم؟