So as we talked about the abstract classes and abstract methods inside those classes, they actually
have to be extended.
So that's the point of an abstract class.
Well, it's actually fine for an abstract class to sit around an extended.
You don't really need to do that.
But then there is no point of such class.
But if you extend you inherit an abstract class, then you need to override those abstract methods and
actually implement them.
Now there is also like another side of the coin to this.
So another spectrum which are final classes and final methods.
And as you can guess, probably these are the classes and methods that you can't extend.
So here we've got an online payment processor.
An abstract class that implements an interface and it is used and it is prepared so that if there is
a need to implement another payment processor except stripe and PayPal, this can be easily done.
But there might be situations when maybe either PayPal or maybe stripe is introducing a new version
of their system, and for some reason, someone on your team thinks it's a good idea to just extend
the stripe processor and maybe call it stripe processor version two.
And maybe this just changes the validate API key or any other behavior.
So anyway, generally someone thinks that creating class like this is a good idea.
So I would advise you against doing that and against having a complex inheritance tree.
I think that inheritance works best only if you use it the way that there is an abstract class that
is there to provide some functionality and show you the way of how something should be done, and then
it is a good idea.
Like this class is a good idea, but then endlessly extending a base class to add more and then add
even more functionality, this can really end up creating a mess.
And if you want to stop people from creating a mess, especially if this might be some junior coworkers,
you just mark a class with the keyword final and if anyone tries this, if they want to extend it,
this is just a fatal error.
You cannot extend a final class.
Now you can also mark methods final.
So let's remove that because this was a bad idea.
Now some people even default to marking the classes as final, and only if you need to extend a class.
You need to then remove the final keyword and possibly also explain why you would like to do that.
Now also, just keep in mind that if you really feel the urge to extend a class that's already there,
just think if maybe composition isn't a better idea, because with every single class, you can actually
pass it through a constructor.
And that is actually a very good pattern.
Now, another useful thing in modern versions of PHP are read only properties.
So currently, for example, with this one we've got the constructor property promotion.
So this is an API key property on this class that can also be accessed in the child classes.
But if the class should be created with a key and then not modified, and you want to make sure the
property is never modified, then you can add a read only keyword to it.
And if anyone attempts to change the value of this property, obviously after it was initialized, this
would also raise a fatal error.
Now, I also think that if you are passing some data to an object, and this data is credentials, keys
or anything that shouldn't change.
You should default to those properties bank read only and only then try to remove read only attribute
read only keyword.
If actually this property should change and that's expected.
Since we are talking about modern PHP features, let's also mention a union type.
This is something like this.
So we are using this.
Let's call it a pipe operator to tell PHP that specific function argument.
This also works with return types and properties that it can take different types, because previously
we were limited to float and if for any reason you've got some data that was an integer.
This just wouldn't work.
So by using this pipe operator you can specify multiple acceptable types.
You can specify as many as you want actually.
But with numbers those two typically can cover most of the cases.
So now after this change we should actually change that everywhere.
Now the union type can also be used for accepting different kinds of values.
By different I mean not just numbers that are float or integer.
But you can add this type to accept maybe an array and also a string.
And to show you an example of that, maybe you can have this process order function that apart from
the amount can also accept items.
So this can be string one item or just an array of items.
So let's call that items.
And here we're going to do some processing with the items.
So after accepting different types of arguments you can also check what is the actual type.
So we can use if is array.
That's the function that will just tell us if that's an array or not.
So if this is an array we can use implode to convert this to a string.
So we use a comma and items.
Otherwise the items list is just a string.
So it is the items.
And here we will always output that.
We are processing order for items.
And we're going to list them.
So maybe this can be a book.
But here we've got an array to be book and movie.
And I said let's have yet another array.
Maybe apple and orange.
Okay.
Let's see if this works.
Okay.
So here are the orders.
So we have just one item and then we've got multiple items.
So everything works fine.