I’ve always struggled to visualize the bootstrapping of Cake. Middleware has made this more difficult, but also more critical. So here is a simple overview of a request to a CakePhp 4x application showing the major pre-controller classes and their interactions. Forgive any hand-waving parts
A written walkthrough follows the diagram.
Here is a diagram for AuthtenticationMiddelware as it would act between step 16-17 or between step 18-19.
If your not familiar with a sequence diagram
- The boxes represent class objects.
- The vertical dotted lines are the lifespan of the objects
- The vertical bars are methods and their lifespans
- Solid horizontal arrows are method calls
- Dotted horizontal arrows are method returns (mostly)
- creates an instance of App\Application.php (#1, #2)
- creates an instance of Cake\Http\Server passing it a reference to Application (#3)
- when Server constructs, it stores the Application reference and make a Cake\Http\Runner instance (#5, #6)
- index calls Server::run() without the optional Request and MiddlewareQueue instance arguments (#7)
Now the things you’ve previously added to Application.php are going to come into play.
- Server calls Application::bootstrap (#8)
- Plugin.Application.php bootstraps will be run here as well but only after the main Application’s process. Good thing because that is where the plugins are loaded. So they all arrive on the scene just in time to participate.
- Server uses ServerRequestFactory to collect all the php super global values and turn them into the familiar ServerRequest object (#9, #10)
(#11) makes use of another configuration method that you can modify in Application.php
- Server calls Application::middleware() and provides a MiddlewareQueue to collect all the middleware you have configured for you application (#13)
- Server triggers the buildMiddlewareEvent
- Both #8 and #11 provided times when you might have registered listeners for this event. I assume any of the middleware would be logical listener candidates.
With these two Application.php configuration calls done and all the other major pre-controller environment pieces in place, we’re ready to dive into the middleware tunnel and see if we can swim through to your application.
- Server calls Runner::run() with the middlewareQueue, Request, and Application object references as parameters (#14)
- Runner calls its own handle() method with the Request as an argument (#15)
This starts a recursive process where each middleware on the queue gets its process() method called, receiving the request and a reference to Runner as arguments. I’ve only shown two generalized middleware, but any number is possible.
##Details of Middleware::process
Each middleware has an opportunity to work on the incoming Request and outgoing Response. The process goes something like this
- evaluate or modify the request
- if the request doesn’t need to go further, or shouldn’t go further
- make a Response and return it. Earlier middleware will see this returning response and can act on it
- if the request is ok or of no interest to this middleware
- call the Runner’s handle method with the Request as an argument
In this way we will either recursively travel along the middleware queue, or at some point stop the recursion and return to the requesting-client.
If the last middleware on the cue makes its call to Runner::handle(request), the runner will see that the queue is empty and will call Application::handle() (#20). The Request will again be passed as an argument. But it is no longer the simple super global scraping (#10), it’s now been reviewed and modified by all the middleware.
One of these is the important RoutingMiddleware which maps URIs to Cake controller/action pairs and does all the other familiar query and attribute parsing.
If you are using the AuthenticationMiddelware, your request will now have several attributes related to visitor identity and how the visitor was determined to be allowed or disallowed.
- Application::handle(request) gets the proper controller from a factory class (in a process not diagramed) using a reference to the Request object as an argument
- Application calls the controller factory’s invoke() method with a reference to the newly created Controller as an argument
The Controller is the target controller of the request. It will behave as you designed it and will eventually produce a Response object; typically a rendered template.
This response will return up through the middleware wormhole that was created by the recursive handle() calls. Each middleware will see, and have the opportunity to work on the response.
Finally, when the response arrives at index.php, it is sent to the ResponseEmitter, prepared for network transmission, and sent to the original caller.