Magic methods are okay in the right context
This episode is brought to you by Mailtrap. More on them later.
Welcome to No Compromises. A peek into the mind of two old web devs who have seen some things. This is Joel.
And this is Aaron.
I was writing unit tests the other day for an external library... Well, not for an external library, but it was being called inside of this code and I was injecting it. I think I've complained about this before. That sometimes when you want to mock things out and they're like a final class, and you're just like, "What?" That makes it much more difficult to mock this stuff correctly and to modify it. Well, I ran into a different challenge too, which was mocking out this SDK that they had created where there was a property and a method named the same thing. It was impossible really, as far as I knew, to mock out that property's access and also mock out the method. And it turns out the reason why they did that is because both of those were done with magic methods. So there is a __call for the method name and there's the __get for the property value. And really then, because so many other things were used magically as well, it was really difficult just to mock out the get or set because then you'd have to mock out every single call of get and all that stuff. That just got me thinking like, "Should we even be using these magic methods and properties?"
Are you trying to ban magic? Is that what's happening right now here, Aaron? You just don't enjoy the satisfaction. Well, it's interesting because, from a developer experience, it's kind of nice. But on the other hand, from testing and maybe even a maintenance experience, it's like, "Argh! This is definitely not nice." But what you were talking about is a third party client library. Like, in Laravel itself Eloquent models, we have something similar. Maybe not the weird combination of magic properties and methods, but certainly properties. What are your thoughts on those? Do you get the same vibe or is that different?
It's both the same and different. I would disagree because that's just what I do I guess. I'm with you saying that it's a good developer experience or whatever. Having used magic properties and methods, I think it's actually a worst developer experience unless you know exactly what is the magic. If you are trying to look at something that has all the methods defined, all the protected or all the public methods, all the public properties, it's easy to figure out what's there. But if you're in Eloquent and you're not using something like IDE Helper, which puts all the annotations and stuff, how do you actually know what names of each of the properties are? Well, you have to then go one step further and look in the database I guess. Because there's no properties on the model that you can be like, "Oh, what was the name of that? Was it completed_at or date_completed? I don't remember which one."
It's a good point. Because it's like the tooling, you mentioned IDE Helper, in PHPStorm land there's that Laravel Idea plugin, that will introspect some of these things for you even without DocBlocks. It can make the experience almost as if they were declared properties, right? From the perspective of your IDE or code editor or auto complete, you can hit arrow and see a list of what they are. You could start typing, in your example, you could start c-o-m and you could see, "Oh, it is completed_at." Maybe that's a distinguisher here too, is like, is there tooling to support the authoring experience with these magic properties? Because I like using them in Eloquent. I mean, what would be the alternative? It would feel weird to me to come into a project where somebody had actually declared all those properties on the model class.
Yeah, I agree that that would be weird in Eloquent and Laravel land. But it doesn't mean that we can't look at some of our things and say, "It makes sense in this situation, but not others." For example, I'm fine with them being on Eloquent, but I don't necessarily like the dynamic facades, for example. It's hard to know what exactly is there unless you have tooling. And I know I'm getting nitpicky here, but I think there's a difference between tooling where it's just the language can understand itself. And, I'm going to call it annotative tooling, which is like IDE Helper and that Laravel Idea plugin where it knows something that isn't necessarily in the code. It's only after processing the code that it can understand that so it's not really a static analysis thing per se. I know we're kind of bumping up against a lot of different things here. But I think for certain things it's okay and other things it makes it much more difficult.
Well, let me give an example because I think we're on the same page. Using IDE Helper model properties, great. We use it all over the place. What we don't use, which is actually quite similar, is request. When you have a request coming into a controller, you could do request arrow property or even a form request. Same thing, it's a dynamic access to a property that's there. But there isn't anything like ID Helper, at least not that I've seen, that will type those things for you. And some of that is because it could actually be dynamic. It's not as static, it's like table in a database backing this model.
Well, the other thing is, I can't remember off the top of my head, but let's just say the form request is wrapped around something internally, which has this input and that's how you get stuff. Well, what if you have a property in your form field called input? Then what would happen when you call this input? Is it the actual input underlying thing or is it your property? I would hope it's the underlying thing but then how do you get your property? They're going to have to do something else anyway, so there's going to be some sort of overlaps here.
Yeah. I would just say don't name a field input, that's just bad. But I get your point. Like, there's going to be some potential for overlap with a dynamically accessed property on a class as big as the request object. There's a little bit of a tangent, but I want to give you a chance to disagree with me or something. But remember there was a project recently where I was toying with an accessor on a form request that was strongly typed where it would... Because validation had happened, right? We always validate in our form request. So we know these fields, for example, are present and they're a string and maybe this is an array of ints or things like that. So there's a guaranteed contract that if the request makes its way into the controller, all of those properties were true. I was using these crazy DocBlocks with array shapes where you could actually name all of the keys. And did you like that, Aaron? What was your thought on that? I was just playing around.
I can see the point, I just didn't see the value of all the work. Maybe it helped you with one or two things but in general, all that work to create those typings... Which I'm a huge fan of types, like parameters and all those different things. But when you're actually putting in DocBlocks only for this other piece of code which, I mean you already have a good idea what that is and how those should work, as well as... Like, that wasn't useful. That was, I think, on the rules but we don't use the rules. We don't consume the rules, so why would we care what the shape is?
Right. Well, this was... Just one more little nuance on this because I think it just kind of lined up with this topic. The request object in Laravel has some typed accessors. You can say arrow boolean and then give it a request property name and it will give you back a boolean. Everything that comes in an HTTP request is a string but there were some typed helpers. These typed helpers did not exist on the form request. I take that back, a form request is a request. But they didn't exist on the validated specific accessor for the form request, which we always like to use just as a sanity check. We're only going to access stuff that's been validated. We couldn't do that. So I was just playing around with it and I wasn't convinced either. I was just having fun.
You know, maybe this is an opportunity for some tooling. You know, if there was something like an IDE Helper that could look at your rules and create a typed object that comes out... I know there are packages that do this. I think Spatie makes a DTO package, Laravel data or something, where you can more explicitly define this. I haven't actually tried that. I looked at it and there's some things about it that give me pause. But I don't know, I think what you landed on is it feels brittle, right? Every time you change a rule and have to remember to go change this DocBlock, which is true of models. If you have a migration you got to update your DocBlock but there's a tool that does it for you. Whereas, if you're doing it manually, it just feels not as good.
Yeah. I think there are some places for the magic methods and properties and stuff, but it usually seems that there has to be additional tooling to make that visible to someone who isn't you. I don't necessarily like that. I mean, many times in my career I had reached for that, especially when I thought I was more clever. And that's, again, a rule we always bring up is, "If you think you're being clever, you're probably not."
It'll never come back to bite you, I'm sure.
This episode is sponsored by Mailtrap, an email delivery platform that developers love. An email sending solution with industry best analytics, SMTP and email API, SDKs for major programming languages and 24/7 human support. Try for free at mailtrap.io.
There are many things that indicate to me that I'm getting old, and I've mentioned them a few times I think on the podcast. But one of them that's just recently happened is I'm interested in silverware now.
Okay. Say more about that.
I have a set of silverware I bought, I don't know, like two decades ago and they work perfectly fine. But recently I've been looking on Etsy at silver silverware and I'm just like, "Oh, wouldn't it be nice to have a heavy butter knife?" Well, I don't ever sit down at my table and then weigh my butter knife and then, "Oh, this is so nice." Like, I grab it from the drawer, wipe something on something and throw it in the sink. I would break all these silverwares, I think.
No, you don't do that because you don't have something to really savor. If you had that nice set then you would.
Oh.
It would be more of an event every time you butter your bread or whatever it is you do with a knife.
Let's be honest. Just like Joel said, you're probably listening to this at 1.5 or two times speed, maybe two or three in a row. But you know there is a show.nocompromises.io page, right?
We'd really appreciate it if you share the show with others. Because maybe you really enjoyed it or maybe out of spite because you know somebody will disagree with our topic.