Chris Aprea recently wrote a great post on why WordPress’ (continued) support of unsupported versions of PHP, especially PHP 5.2, is preventing WordPress developers from taking full advantage of the way the language has evolved over the last 8 years or so.
Two of the best features in PHP, which WordPress developers have generally shied away from since they were not fully supported by PHP 5.2, are SPL autoloaders and namespacing. These two complementary features make it easier to use small, more manageable, and more easily reusable classes. Technically, PHP has had autoloading since 5.0, but the addition of namespacing in 5.3 allow for the type of autoloaders used by composer and the PSR-4 standard autoloader. There are workarounds for 5.2 that do not use namespacing, but this is 2015 and no one should be using a slow, unsupported version of PHP.
Understanding how namespacing and autoloaders work will help you get comfortable working with PHP libraries that were not written for WordPress, but do follow the established PHP standards. You can, however, use autoloaders without namespaces or without following the latest standards. For example, Carl Alexander discusses how to build an autoloader that follows the standards for class naming used in WordPress core in one of his excellent tutorials.
In this article, I will discuss how to follow the PSR-4 standard for class and file naming. By doing so, you’ll find that this standard not only makes it very easy to use a class autoloader, but it also makes your code much more understandable to other developers.
In this article, I won’t go into too much detail on the PSR-4 standard or how namespaces work. I will, however provide a basic introduction to both in this article and will cover both in more depth later on this month in Torque. I recommend this great article on using namespaces by Dayle Rees and this article on structuring a PHP library following the PSR-4 standard by Phillip Brown.
Namespacing
In functional programming, we essentially use a pseudo-namespace in the form of a unique slug for our functions. The unique prefix is an imperfect solution to the fatal error that can occur when two plugins, themes, or libraries add functions of the same name. This practice extends to class naming as well, when namespaces are not used.
When you use namespaces, you are able to use short, descriptive names for your classes, since namespaces act like the unique slugs for functions in order to avoid name clashes. Namespaces also act as a way of descriptively organizing your classes and, when following the PSR-4 standard (and its predecessors), your file structure.
When following the PSR-4 standard, the class name and the file name must be the same. Instead of having classes with names like slug_crud and slug_api, we could just have classes named “crud” and “api” both under the “slug” namespace.
In addition, the namespace has to be the same as the file name. Because namespaces can be nested, like directories, they reflect your directory structure. So, you may wish to create multiple, simple-purpose classes inside namespaces, under your root namespace. For example, you might have a namespace called api, instead of a class called api, and in that namespace there may be multiple classes. Their full namespace would be slug/api.
So, a class in this slug/api namespace called “authenticate” would be in a file called “authenticate.php,” and reside in the directory “api.” Here is what the class would look like:
<?php namespace slug/api; class authenticate { public static function check() { //do stuff } } ?>
We could use the static check method of this class, outside the class, like this:
/slug/api/authenticate::check();
Inside the class, we would use the method the same way as always.
Also, notice that the the root namespace slug was prefixed with a “\”, which is only needed when it’s not in the slug namespace. Similarly, inside the slug namespace, classes from outside that namespace will need to be prefixed with the backslash.
One cool thing about the PSR-4 standard is that the root namespace does not have to be in a directory of the same name. Typically, the root namespace of a library is in a directory called “src.” This allows the main directory of the library to just contain the “src” directory, a “tests” directory, the composer.json file, and the readme. If your library is a plugin, you will need the file with the plugin header there as well. That file is also a great place to initialize your autoloader, which I will discuss in the next section.
Autoloaders
Class autoloaders load all of the files containing your classes for you, so you do not ever need to worry about including or requiring those files. Putting one in place is simple, and respects the proper division of labor between humans and computers, where humans write creative code, and computers do menial, repetitive, and easy-to-screw-up work.
Autoloaders are very easy to use as well, which I will walk you through below. You simply need to add a file with the standard autoloader, which you can find here, include that file, and then register the namespaces and their root directories.
I like to set up my autoloader in a function hooked to “plugins_loaded” that used a simple loop to load an array of libraries. It also will automatically include a file called “functions.php” if it exists in the namespace’s root directory.
The loop uses a simple array of “library_name” => “namespace,” or “library_name” => false, if the namespace and library name are the same. Here is an example of how it works:
<?php add_action( 'plugins_loaded', function() { $class_dir = HSYNCC_PATH.'classes'; include( $class_dir . '/jp_auto_loader/src/jp_auto_loader.php' ); $libraries = array( 'jp_keyed_request' => false, 'hsync' => false, 'jp_keyed_request_cye_save' => 'jp_keyed_request\cye_save' ); $classLoader = new JP_Auto_Loader(); foreach ( $libraries as $lib => $namespace ) { $root = $class_dir . '/'. $lib .'/src'; if ( ! $namespace ) { $namespace = $lib; } $classLoader->addNamespace( $namespace, untrailingslashit( $root ) ); if ( file_exists( $root . '/functions.php' ) ) { include( $root . '/functions.php' ); } } $classLoader->register(); }, 3 );
The most important line in this code is “$classLoader->addNamespace( $namespace, untrailingslashit( $root ) );”. This line basically tells the autoloader “this namespace is located here.” If you only have one namespace in your library, that’s basically all you need, besides initializing the class, and the last line “$classLoader->register();.”
This is easy and it will make you better
Classloaders and namespaces take a bit of time to get used to. I hope this brief introduction (and the articles I linked above) will get you properly acquainted with them. Once you are, you will have more organized code and it will be easier to create smaller and more simple classes, which are more reusable and focus on a single responsibility. These are all steps to writing better, more manageable code, which should be all of our goals.
6 Comments