Friday, November 25, 2016

Object-Oriented Autoloading in WordPress, Part 2

Object-Oriented Autoloading in WordPress, Part 2

In the previous tutorial, we covered a handful of concepts, all of which are going to be necessary to fully understand what we're doing in this tutorial.

Specifically, we covered the following topics:

  • object-oriented interfaces
  • the single responsibility principle
  • how these look in PHP
  • where we're headed with our plugin

In some series, it's easy to skip tutorials that may not build on one another; however, this series isn't intended to be like that. Instead, it's meant to be read in sequential order, and it's meant to build on the content of each previous tutorial.

With that said, I'm assuming you're all caught up. 

Getting Started

Even though I might have mentioned this in the first tutorial, I still like to make sure we're all on the same page with regard to what we're doing in each tutorial and with what software you're going to need.

Our Roadmap

So in this tutorial, the plan is as follows:

  1. Examine the code that we have written thus far.
  2. Determine how we may be able to refactor it using object-oriented techniques.
  3. Provide the high-level outline for our implementation.

Ultimately, we won't be writing much code in this tutorial, but we'll be writing some. It is, however, a practical tutorial in that we're performing object-oriented analysis and design. This is a necessary phase for many large-scale projects (and something that should happen for small-scale projects).

What You Need

If you've been following along, you should have this already set up. But to make sure, here's the short version of everything you need:

  • a local development environment suitable for your operating system
  • a directory out of which WordPress 4.6.1 is being hosted
  • a text editor or IDE
  • knowledge of the WordPress Plugin API

With all of that in place, we're ready to work on the code shared in the previous tutorial. So let's get started.

Analyzing the Code

The very first thing we want to do is analyze the current state of our autoloader. It might seem like a lot of code to paste into a single block of code, but that in and of itself shows us that we have some work to do.

With that said, here's the current state of our autoloader:

At this point, remember that the single responsibility principle states the following:

A class should have only one reason to change.

Right now, we don't even have a class, let alone multiple individual methods that have only a single reason to change.

And though it might make sense to start by breaking this autoloader method into smaller, individual methods, let's start from a higher level and begin thinking about an autoloader in terms of an interface. Then we'll drill down into creating a class (or classes).

Object-Oriented Analysis: Responsibilities

Recall from the previous tutorial that an interface is defined by the PHP manual as follows:

Object interfaces allow you to create code which specifies which methods a class must implement, without having to define how these methods are handled.

Given the code and the definitions above, let's think about what an autoloader needs to do from a more modular perspective. Specifically, let's break it down into points that represent what might be enough to change. No, we may not use all of these points, but this it's why it's called analysis. We'll work on the design later.

The code does the following:

  1. Validates we're working explicitly with our namespace.
  2. Splits the incoming class name into parts to determine if it's a class or an interface (so $class_name is a poor variable name).
  3. Checks to see if we're working with an interface file.
  4. Checks to see if we're working with a class file.
  5. Checks to see if we're working with an interface.
  6. Based on the outcome of the above conditionals, generates a file name.
  7. Builds a file path based on the generated filename.
  8. If the file exists at the generated name, includes it.
  9. Otherwise, the code generates an error.

Thus, the above code does nine things—that is, it has at least nine reasons to change—before it's done completing its work. 

This should go without saying, but this particular function is a perfect example that we can refactor to demonstrate object-oriented analysis, design, interfaces, and implementation.

And this raises a question: Where do we even begin?

Object-Oriented Analysis

At this point, it's fair to say that we can begin doing object-oriented analysis—that is, looking at what potential classes we may have and how they interact—given everything we've listed above. Remember, we also want the single responsibility principle to help guide us in our decision making.

At this point, we're not terribly concerned with how the classes will communicate with one another. Instead, we're more focused on creating classes that have a single reason to change.

With that said, I'm going to provide a sample set of classes that I think might work. Before going any further, look at what we've done and attempt to come up with your own list. Then we can compare notes.

A Word About Skills

Note that you may have a better idea than what's listed below, or you may take something away from what we've shared. Regardless, this is a learning exercise. We're attempting to improve our code, our organization, and ultimately become better programmers.

Our Potential Classes

Given what I've listed above, I've come up with the following classes:

  1. Autoloader. This is the main class that's responsible for ultimately including our class, our namespace, or our interface. We'll call this class. The rest are classes that will take care of necessary work that this one class needs to include the file. 
  2. NamespaceValidator. This file will look at the incoming class, interface, or what have you, and will determine if it's valid. This will give us the deciding factor if we can proceed with the rest of our code our not. 
  3. FileInvestigator. This class looks at the type of file that's being passed into the autoloader. It will determine if it's a class, an interface, or a namespace and return the fully-qualified path name to the file so that it may be included.
  4. FileRegistry. This will use the fully qualified file path ultimately returned from the other classes and will include it in the plugin.

And that's it. Now third-party classes in our plugin only need to know about the autoloader class, but the autoloader will need knowledge of another class, and other classes will need knowledge of yet other classes.

There are ways to handle this (using dependency injection containers, but that's beyond the scope of this project). But what we'll aim to do through our code is minimize how many classes know about one another.

Object-Oriented Design

At this point, different developers, firms, agencies, and teams will take a different approach to how they design the system on which they are working.

One of the most common ways to go about doing this is to use something called a UML diagram. Though it's useful, it's not something that's worth doing within the scope of this tutorial because it will require a whole other tutorial to explain all of the pieces.

So for the purposes of our tutorial, and since we're working with such a small amount of code, we'll try to stub out how each of the above classes may work before we implement them. This way, we'll get an idea of how we can organize our code.

Note that we won't be namespacing any of this code just yet, and none of this code should be implemented or tested against WordPress just yet. We'll get into that in the next tutorial.

Let's start with the Autoloader and work from there.

Autoloader

Remember, this class is responsible for including the necessary file. This is the file that will be registered with the spl_autoload_register function. 

Note that this class depends on the NamespaceValidator and the FileRegistry class. We'll see each of these in more detail in just a moment.

NamespaceValidator

This file will look at the incoming filename and will determine if it's valid. This is done by looking at the namespace in the filename.

If the file does in fact belong to our namespace, then we can assume it's safe to load our file.

FileInvestigator

This class is doing quite a bit of work, though part of it is done via very simple, very small helper methods. During the course of execution, it looks at the type of file that it's passed. 

It then retrieves the fully-qualified filename for the type of file.

If there's a file that can be refactored a bit more, then this is it. After all, it attempts to determine if we're working with a class, an interface, or a class. A simple factory might be better suited to this.

When it comes time to implement our code, perhaps we'll refactor this further. Until then, this is a preliminary design that may work well enough.

FileRegistry

This will use the fully-qualified file path and include the file; otherwise, it will use the WordPress API to display an error message.

Another alternative to using the WordPress API would be to throw a custom Exception message. That way, we'd be able to completely separate or decouple our code from WordPress.

Once again, this code is a carry-over from the initial autoloader. During implementation, we may change this, as well.

Conclusion

Alright, so we've looked at the existing code for our autoloader, and then we've stubbed out some potential code that we can use based on some object-oriented analysis and design.

Is the solution that we're working toward more maintainable than what we have? Absolutely. Is this going to work within the context of WordPress and our existing plugin? We won't know until we begin to hook this up into our plugin.

As previously mentioned, there are still some areas in which we could possibly refactor this code. If we hit these type of issues when implementing our code in the final version of our plugin, we'll take a look at doing exactly that.

Whatever the case, the code that we have now should be more readable (though we still have DocBlocks and some inline comments to introduce) and more maintainable and even more testable.

With all of that said, I hope that this has given you an idea as to how to take a long method and break it into more purpose-driven classes. Sure, having multiple classes might feel weird at first, but that doesn't mean it's a bad thing. Have more files (and thus classes) with less code than one file with a lot of code is better.

Embrace the counterintuitive nature of object-oriented programming in this regard. In the next tutorial, we're going to be returning to our plugin and will be working on implementing a variation of the code above. We'll likely be debugging some of it as well. After all, rarely do we get it right the first time

Until then, if you're interested in reading more about object-oriented programming in the context of WordPress, you can find all of my previous tutorials on my profile page. Feel free to follow my on my blog or follow me on Twitter where I frequently talk about both.

Resources


No comments:

Post a Comment