Right guys?
So next up I'd like to introduce a super interesting concept that is called Middlewares.
That's something that you are going to find in every single HTTP framework, not only in PHP frameworks
like Laravel, but also in Node.js, Python, Ruby, everywhere.
So what is middleware?
So let's take a look at those two example diagrams.
First is how it works.
So you need a router.
We have a router class right here.
And the concept of Middlewares is that you would like to run some code in the middle.
So before you run the route code and that's everything.
And you'd like to have a couple of those middlewares.
And every single one of them does some specific job, has some specific responsibility.
And the way it is constructed is that the Middlewares they call the next middleware in line.
So it's about how the middleware should be implemented in the router.
So it is implemented the way that you're going to add the middlewares to run before a route.
And it is a responsibility of the middleware to call the next one.
Now not always the middleware calls the next one.
It can break the chain.
That's another feature of middleware.
And let me just come up with an example of what you can use Middlewares for.
So in our case we can have two obvious use cases that would be checking if the user is authenticated
and the other one is sharing the user object with all the views.
So let me jump into our front controller.
That's one of the obvious use Cases that can be a middleware that runs always, that just shares the
currently authenticated user whether he is logged in or not with the views, so that we just don't run
this code in the front controller.
But another middleware can be an auth middleware, and this can be applied conditionally on specific
routes.
That's another concept that we can have global middleware that needs to run at all times.
And sometimes we might have specific route specific middleware like auth that will basically require
the user to be authenticated.
If he is not, the middleware will break the chain by, for example, redirecting to a for one page.
Now currently we just do it manually.
Like in this command controller we're getting the user, and if the user is not signed in, we redirect
to unauthorized page and this can be done by middleware.
It will just simplify our logic.
Another case is CSRF tokens.
I think this should be automated for our app to be secure.
So every single time we encounter a request that's different than Get head or options, we should just
check the CSRF token.
So if you would forget to generate the token in the form like in here, then you will just know what's
the problem, because the middleware will just take care of making sure the token is generated and verified.
Okay, so after this introduction, let's just jump right ahead into implementing the middleware so
you can see what's the best way to approach this, and then in the next videos we're going to be adding
some middleware.
For example, for the CSRF tokens, authentication, sharing user and all the future useful appliances
of middleware.
Let's jump to implementing the middleware logic.
Okay, so let's try to implement the middleware starting with an interface as the first time we are
using an interface, and we are using it because we would like to enforce the same structure on every
single middleware, as it is important that every single middleware accepts the next one to call.
Let me create this interface.
Maybe things will become much more clear as we go.
I'm just going to call this file middleware PHP.
Let me zoom in a little bit and it's going to have a namespace that's just call.
And I'm creating an interface called middleware.
It's simple because it only has one public method called handle, and it accepts an argument of type
callable that is.
Next.
Essentially what this means is this should be the next thing to call.
And if you look at this diagram, this is actually super simple.
The first middleware should call the next one that is registered.
And the second middleware, if we have only two, should actually call the root code.
So what will change in our router is instead of just calling the root like we do right here explicitly,
we're going to be calling the middleware first.
And that's the job of the middleware to eventually call the root.
If this sounds complicated, I'm sure it won't look complicated once we have some examples.
So then you just need to bear with me.
Okay, guys.
So now let me jump to the router and let's make some changes here.
We've got an array with routes, and now we need to also keep the middleware someplace.
And as I said I like the global middleware and also the route middleware which is applied per route.
Both are associative arrays or maybe not.
The global middleware would just be an array of middlewares.
And the changes we'd like to make is to this add method that adds a route.
We're going to have another parameter which is an array, and it would just be called Middlewares.
This is Middlewares that's initialize with an empty array and to every route.
We're going to add this.
So we just know what middleware should be applied to a route.
Now we should add some methods that will just allow us to add those middlewares first to add the global
ones.
So the argument here would be a string y a string because this would be the class name.
So currently in the first implementation of Middlewares, we would be responsible inside this router
class to create an instance of such middleware.
This is void doesn't return anything.
And well it's quite simple.
We've got this global middleware field to which we add a middleware.
Next up let me add another one called Add Route middleware.
So this would have two parts.
There would be a name, a short name of the middleware.
So we don't have to use the class name every single time.
And also the actual class.
And in this case, inside the root middleware we're going to be adding an entry using specific key.
That's a name.
And to this we're going to add the class name of the middleware.
Okay.
So this was all very theoretical I'm aware of that.
Now it's time that we jump to the dispatch method.
And we can see how we should be getting all the middleware that should be run and how do we execute
it all together.
Okay.
So before we run a specific action, we need to collect all the middlewares that we should be running.
So we have to mix the global and the route specific one.
So at this point we already have a found route.
This means it might have some specific middleware that it needs to run.
Let me just use this unpacking operator and just grab the global middleware.
First it should run as the first middleware.
And then let me use array map to map all the shortcut names of all the route specific middleware into
the actual class names.
That's why I'm going to start with the second argument, which is the route specific middleware.
If I'm right, we've used the yeah, we've used the Middlewares key.
So I wasn't right.
That's middleware.
And let me move that into a separate line like this.
Okay.
And here I'd like the arrow function that would convert the name of a middleware into the actual class
name.
This would be this route.
Middleware.
That's the field that just contains the names or maps.
The shortcut middleware names to the actual class name like that.
So maybe to illustrate what I mean with this mapping, I'd like to be able to do it like this.
I'd like to use the auth middleware.
That's the shortcut name.
And it would mean let's see what can we have.
Um maybe up middle.
Where's auth middleware.
So I would like to create a mapping that would allow me to use this short names that maps to full name
space, and when I add routes, I can just use this shortcut name for convenience, especially if I
need to apply multiple middlewares per route.
Okay, so next up we need to add a function that will just run all the middleware.
And if we think about it, we just need a function that will just run all the steps.
As we take a look at this example, we just need to run first middleware, second middleware, and then
the actual route.
So we need a method that will do this.
Let me call this protected function called run middleware.
It will get an array of all the middlewares.
And finally it gets the callable which is the target the final action to run.
And it can return a mixed value.
So instead of just calling the action now we're going to run this method that will just run all those
steps first middleware, second middleware and then the route.
So how this would work we need to know what are we going to run next that is initialized with a target.
Next up we are going to iterate over all the middlewares in reverse order.
So we do array reverse with Middlewares.
As middleware.
Okay.
So why do we reverse those middlewares.
So I'm reversing this array because it is a common pattern in middleware implementations that the last
middleware actually runs first, or to be precise, the middleware that was added last runs first,
and it kind of wraps all the other middlewares.
Okay, so we need to figure out what is the next middleware to run.
We essentially create a linked list.
So for every middleware we need to create a class.
Remember that's possible in PHP you can create a class based on the variable that contains the class
name.
And on this class we call handle.
We know it has this method because every middleware needs to implement an interface.
And remember that every middleware has this next argument, which is the callable.
So it just knows what is the next middleware to call.
And this just happens inside the middleware.
So basically if we create this chain of middlewares that leads up to a route, this middleware does
whatever it needs to do, and it is the responsible responsibility of this middleware to call the next
one.
So this one will do whatever it needs to do.
And then it calls the thing that's next, which might be a route or another middleware.
And it's implemented this way because if you want to break the chain like someone's not authenticated
and you want to stop the processing, you just don't call the next middleware.
And this is how you break the chain.
And finally this just returns a call to next, which would be callable in this case.
And now after implementing this method we can finally wrap.
This fragment that actually runs the actual action.
And here we're gonna return this run middleware.
Here we pass our array of middlewares that we have just created like that.
The second argument is anonymous function.
Where we need to use a route.
So outside variables are not accessible inside anonymous anonymous functions unless you use this use
keyword.
And here we just paste the code that is executing the action.
So with this middleware implementation I think maybe it's not clear.
But you can see that the worst case scenario we are going to call the action.
So next is initially the action of the controller.
And if there is no middleware defined.
We're just going to run the controller action.
But if there is any middleware defined, we reverse the order of the order.
How the middleware was added to the list.
And then we first initialize the middleware.
We run it passing to it.
The next thing to call.
And inside the middleware you will be responsible for actually calling the next thing on the chain,
which eventually is always our own controller action.
So I think, and I agree that this is kind of complicated, maybe even very complicated.
Anyway, I'd like you to bear with me because when we run this in the next video, we create some simple
middlewares.
You will see how powerful mechanism that is, and I think you might just understand everything a little
better.