I recently released a plugin comprised almost entirely of code pulled from composer libraries. The plugin’s main file (besides the header and license) just includes the composer autoloader and the plugin’s activation function. That’s it.
The rest of the plugin’s functionality comes from four composer libraries: three of which I wrote, and all of which are easily reusable in other plugins or other types of projects. That practical level of code reusability is one of the most exciting parts about integrating Composer into my workflow.
In a recent article for Torque, I gave a general introduction to using Composer with WordPress. In this article, however I’d like to delve a little deeper and discuss how to improve your plugin development and write better code. I will also discuss how to make use of the Composer autoloader.
The point of this article is to show how to use Composer as the cornerstone of a strategy to write better and more reusable code. A point that is actually largely redundant: When code is more reusable it means that it can be used more, and can more easily be released publicly, and therefore used even more. In addition, the more code gets used, the faster its weaknesses are exposed, reported, and fixed. The fact that what I’m discussing actually makes completing projects faster and easier is a fabulous bonus.
Libraries instead of plugins
Embracing Composer means a new approach to writing plugins. It means writing lots of smaller libraries, submitting them to Packagist — or privately via a proxy powered by Satis or Toran Proxy — and letting your actual plugin be just your composer file. You’ll need to manually build the plugin for release on WordPress.org, which is something that can be automated.
By working from composer libraries it’s easier to write a class for a client-site and include it in a publicly released plugin. It’s also easier to share code between plugins and sites without resorting to cut and paste. You could use git submodules, however not only are they a pain to use, but you can’t specify dependencies like you can with composer libraries, which is incredibly important in order to safely reuse the code.
Using the Composer autoloader and why autoloaders are important
In my last article on Composer, I covered the basics of writing a composer.json. The part I didn’t cover was setting up how your library interacts with the autoloader. The autoloader makes it dead-simple to use your libraries in a plugin — or for others to use it in their own plugins or other projects.
The “autoload” section of your composer.json file is actually quite simple to use. This is especially true if you have used namespaces for your plugins, according to the psr-0 or psr-4 standards. If you have followed one of those standards, all you need to do in the autoload section is specify which standard you’re using, along with the root directory for your main namespace.
For example, if you’re using the psr-4 standard, and your library’s namespace was “awesome,” you would use this:
"autoload": { "psr-4": {"awesome\\": ""} }
This presumes that the root directory is the namespace root. If it’s in a subdirectory, for example one called “src,” you can specify it it like this:
"autoload": { "psr-4": {"awesome\\": "src/"} }
Of course, if you’re following the WordPress class naming standard, which is not compliant with PHP standards, you’ll need to specify each class’s name in a “classmap.” For example, to load classes, you would do this:
"autoload" : { "classmap": ["class-slug-foo.php", "class-slug-bar.php"], }
Of course, that doesn’t scale, but neither does not using an autoloader. In my opinion, having to manually include class files, and following the WordPress class naming standard is an impediment to writing small, easily reusable classes. I’m not convinced the “class-<vendor-slug>-<class-name>.php” naming standard was ever really intended to extend beyond core development.
Using (either Composer’s or a separate) autoloader makes it easier for you to abide by the single responsibility principle. Tom McFarlin recently wrote a great article on this principle, highlighting how it’s generally misunderstood. In it, he makes this a great point:
Rather than ask:
“Is this module doing one thing and doing it well?”
We should be asking:
“What would require this module to change? Or, rather, under what conditions what this module need to be changed?”
Until we have a single, definitive answer for each function and its class, then we’re not properly employing the Single Responsibility Principle.
This leads to having lots of files in your project, which is a problem if you have to manually include each file, load the class, remember to remove that code when removing the class, and avoid making any mistakes in the process. When you follow the standard, autoloaders remove the need for all of that.
What about files that don’t contain files?
If your library contains files that are not classes — like a functions file, for example — you can include those via the autoloader as well. For example, to include a file called functions.php in the root of your library, you would do this:
"autoload" : { "files" : ["functions.php"] }
Once you have the autoloader setup properly, you just need to include that one file, like this:
require_once( 'vendor/autoload.php' );
Dependencies
One of the advantages of Composer is that you can specify which other libraries a library requires, and Composer will install them for you. Again, this leads to more reusable code. By splitting my classes into smaller chunks, it allows me to move a lot of common “utility” classes and functions into new classes. Composer makes it easy to share them between different projects without worries.
Listing dependencies in your composer.json is something I covered extensively in my last article on Composer.
The one difficult thing is when another plugin is a dependency. Plugins as a dependency can be accomplished for any plugin in the WordPress.org plugin repository using wpackagist, which auto generates a composer.json file. If the plugin has a composer.json in its GitHub repository, it can be installed from there.
The fact that there is more than one way a WordPress plugin can be added as a depenency can create an issue. What if a plugin lists another plugin as a dependency using wpackagist, and then in your package you specify that same plugin as a dependency via its GitHub repository, in order to get the latest version? I’ve been in that situation and it didn’t end well.
I don’t have a great answer for this problem. Rather, I don’t have a solution that is with in my power to implement. In the meantime, I think plugins should only be listed as dependencies when the composer file is managing a whole site, not just a plugin or theme.
Hold on, what about PHP 5.2?
I know right now some people are getting upset because autoloaders and namespaces are not supported in PHP 5.2, which WordPress supports. The plugin I referenced above adds endpoints to the WordPress REST API, which requires PHP 5.3 or later.
That said, beyond the security and performance issues, the biggest problem with continuing to support PHP 5.2 is that it leads to writing big, unmanageable classes. This is because we’re missing the features of 5.3 and later, which makes these things so easy.
I know I don’t get to decide what version of PHP that WordPress supports. But, I do not feel the need to bring the quality of my work down for those who choose, willfully or not, to use a version of PHP that has reached its end of life.
Slowly leaving this glass house
I’m just as guilty as most WordPress developers of violating the single responsibility principle, and moving my not-so-incredibly reusable code between projects via copypaste. However, I am trying to get better. Recently I’ve been trying to write smaller, more manageable classes, and construct my plugins from separate libraries that I published on Packagist.
It’s a new way of working, but I’ve already seen the benefits. I hope over time it will help me share more of what I write for specific projects for reuse by the public. I see it as a pragmatic approach to improving as a developer. It will make my life easier, and allow me to spend more time on writing new code and opening myself up to more people using and finding the shortcomings in my code.
That after all, was the goal for embracing this approach: writing better and more reusable code.
4 Comments