Series Contents

Part 2. Refactoring the Service Loader.

One of the most important things you can do as a developer is refactor. When you get to a point where a piece of code is working stop and look for ways to make it better. This may encompass things like removing duplicated code, breaking up large methods or classes, or even restructuring to make things easier to locate. These types of changes will make your life easier later on when you need to add functionality or fix a bug in your code.

Lets do some refactoring.

Encapsulation.

We are currently using php to do a directory search of the file system for things to load into our Container. Lets encapsulate this functionality and abstract it. Doing so will make it more understandable and push a lot of the low level stuff like checking if directories exist, filtering out ‘..’ and ‘.’, and building paths into helper methods.

There is a great library from Symfony that can help us called Finder. Finder searches the file system for files and directories using a fluent interface and returns the results as a PHP Iterator. Installing it is easy using Composer.

Now we can go back to our Loader files and refactor some code so that we can utilize this library.

Duplication and Refactoring.

All three of our Loader classes basically do the same thing. They all get files from a directory and load definitions into the Container. Right now we call each type and location explicitly. This is code duplication that we can get rid of. We will need to change how we deliver these paths so that we can load the definitions more dynamically. One method to load them all.

Lets start by rewritting the ServiceLoader class. We will be reusing this class to do all of our loading for us. First thing we need to do is add a use statement for the Symfony Finder class so we can utilize it. Add the following just under the existing use statements.

Now add a private property called $finder to the class like so. This will hold our instance of the Finder.

And pass an instance of Finder into the constructor and set it to our new private property.

Next lets refactor our setting.php and put the service paths into an array. This will make it possible for us to pass them to the Loader and let it iterate through them dynamically.

Change

To

We will need to pass the directory names into the ServiceLoader method now that we are going to be reusing it for all loading. Lets change the loadServices method to take an instance of the Application as it’s first parameter, and an array of directories as it’s second.

Add use App\Application as Application; to the use block at the top of the ServiceLoader.php file. Then change the signature of the method like so.

Inside the method lets utilize the Finder object to load the service definitions like so.

In this case we take the finder object and tell it we want to find files. These files have names ending in .php in the root (0 depth) of the directories given. This is where the change to our settings file comes into play. We will iterate over these directories, grabing each file, and pull them in as a required file utilizing their real path name.

Now we see the power of the Symfony Finder. It allows us to search directory paths for defined patterns and retrieve the real paths of those files. All with a couple of lines a code.

Now lets finish it off in our Application.php. First add a use statement to the file for the Finder class just like you did for the ServiceLoader class above. Then remove the loadMiddleware and loadRoutes methods and their calls. Finally change the loadServices class to pass in an instance of the application and the directory array. The loadServices method should look something like this.

Now, one final change. In the services.php file lets add a doc block to the $container variable. While not adding any functionality it adds clarity to what object the variable holds. It will also help some IDEs map the variable to the class that defines it, giving better code completion.

Finished.

We are now loading all of our definitions into the container dynamically with a single method. Very clean and simple. This gives us a lot more flexability in how we define and organize our service definitions. We can group similar services into corresponding files or separate each of them into their own file. There is no Right way to organize them, and how they are organized should be determined by the project’s needs.

In the next installment we will see how to decouple configurations and settings that are environment specific and store them outside of version control.

Code.

See the final code for this article on github. 002 Refactoring the file loader

Next.

Slim-OO Part 3. Decoupling the Configuration.