As things are now, the logic in our page scripts is highly repetitive. They all look very similar. Each one loads a setup script, instantiates a series of dependencies for a page controller, invokes that controller, and sends the response.
Our front controller gives us a place where we can execute the common elements of each page script and remove that repetition. Once the repetition has been removed, we can begin to eliminate the page scripts themselves.
In essence, each of our page scripts follows this organizational flow:
Generic Page Script
1 <?php
2 // one or more identical setup scripts
3 require 'setup.php';
4
5 // a series of dependencies to build a controller
6 $request = new \Mlaphp\Request($GLOBALS);
7 $response = new \Mlaphp\Response('/path/to/app/views');
8 $controller = new \Controller\PageName($request, $response);
9
10 // invoke the controller and send the response
11 $response = $controller->__invoke();
12 $response->send();
13 ?>
Because we have been diligent about always using the same variable name for our controller object ($controller
), always using the same method name for invoking it (__invoke()
), and always using the same variable name for the response ($response
), we can see that the only part of each page script that is different is the central section. That central block builds the controller object. Everything before and after is identical.
Further, because we have a front controller to handle all incoming requests, we now have a place to put the common before and after logic of every page script. That is what we will do here.
In general, the removal process is as follows:
First, we modify the front controller logic to perform the logic common to every page script. We change it from the code listed in the previous chapter to something more like this:
docroot/front.php
1 <?php
2 // page script setup
3 require dirname(__DIR__) . '/includes/setup.php';
4
5 // set up the router
6 $pages_dir = dirname(__DIR__) . '/pages';
7 $router = new \Mlaphp\Router($pages_dir);
8
9 // match against the url path
10 $path = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
11 $route = $router->match($path);
12
13 // require the page script
14 require $route;
15
16 // invoke the controller and send the response
17 $response = $controller->__invoke();
18 $response->send();
19 ?>
We have replaced the line that requires the Router
class file with a line that requires the setup script. (Way back in the chapter on autoloading, we put the autoloader into our setup script, so it should be autoloading the Router
class for us now.)
We have also added two lines after requiring the file $route
to the page script. These invoke the controller and set the response. We use the common variable names for the controller and response objects in this shared logic. (If you chose something other than $controller
and $response
in the page scripts, replace those in the above script. Similarly, if you used a common controller method other than __invoke()
, replace that as well.)
Now that we have added setup, controller invocation, and response-sending work to the front controller, we can remove that same work from each page script. Doing so should be as easy as doing a project-wide search in the pages/
directory and deleting the found lines.
Finding the setup lines probably requires a regular expression, since the relative location of the setup scripts may result in lines that use relative directory traversals. The following regular expression will find includes/setup.php
, ../includes/setup.php
, dirname(__DIR__)
. /includes/setup.php
, and so on:
Search for setup:
1 ^\s*(require|require_once|include|include_once) .*includes/setup\.php.*$
However, finding the controller invocation and response-sending lines should not require a regular expression, since they should be identical in every page script.
Search for controller invocation …
1 $response = $controller->__invoke();
Search for response sending …
1 $response->send();
In each case, delete the found line. It is no longer needed now that the logic has been moved to the front controller.
Once the repeated page script logic has been removed in favor of the same logic placed in the front controller, we can spot check the application. We do so by running our characterization tests if they exist, or by browsing to or otherwise invoking each page in the application if they do not.
After we are sure that the application still works properly, we commit our new code and push it to the common repository. Then we notify QA that we have new work for them to review.
In the examples throughout this book, we have shown only a single script doing the setup work for each page script. Some legacy applications may use more than one setup script. As long as the setup work is identical across each page script, even if it is composed of more than one script, then we can move all the setup work to the front controller.
However, if the setup work is not identical across each page script, we have a problem to deal with. If the page scripts do not enjoy an identical setup process at this point, we should do what we can to address that before continuing.
It is imperative that we make the setup work identical in all page scripts. This may mean including all the different setup work from all page scripts in the front controller, even if some scripts don't need all that setup work. We can remedy this overlap the next chapter if necessary.
If we cannot enforce an identical single-stage setup process, we may have to pursue a dual or two-stage setup process. First, we consolidate common setup work into the front controller and remove it from the page scripts. Extraneous, special-case, or page-specific setup work can remain with the page script as a degenerate but necessary part of the dependency creation work.
In previous chapters, this book emphasizes the importance of consistent naming. This chapter is the point at which that consistency pays off.
If we discover we have been inconsistent in our naming of the controller object variable and/or the controller method name, all is not lost. We will not be able to do a one-pass search-and-replace, but we can still work through each page script manually and change the names to be consistent. Then the newly consistent names can be used by the front controller.