How to cook autoload for the WordPress coding standard

8 mins
150

If you have hundreds of require or include functions in your projects, you should definitely read this article. Autoloading is a really simple tool, but I found many problems with it in other people’s code.

Why do you need autoloading?

Autoload is needed to get rid of require or include functions and stop changing the order of their inclusion forever.

The main solution is to forget about connecting files, you just have to create a file with the correct namespace and path.

Consider the two most popular autoload solutions:

Composer autoload

Composer autoload has a few autoload types:

  • PSR-0 – legacy without namespaces. For example for "WPPunk\\Autoload": "src/" the file must look like src/WPPunk/Autoload/AwesomeClass.php
  • PSR-4 – autoload with namespaces. The most popular solution in the PHP world. For example for "WPPunk\\Autoload": "src/" the file must look like src/AwesomeClass.php and this file must have the namespace \WPPunk\Autoload;
  • Classmap – list of directories for autoloading.
  • Files – list of files for autoloading.

composer.json file:

{
	...
	"autoload": {
  		"psr-0": {
  			"WPPunk\\Autoload": "src/"
		},
		"psr-4": {
 			"WPPunk\\Autoload": "src/"
		},
		"classmap": [
			"src"
		],
		"files": [
			"filename.php"
		]
	}
	...
}

Note: after update the composer.json file autoload section you need to update the autoload files:

composer dumpautoload

After this you just need to include the composer packages in your main PHP-file:

<?php

require_once __DIR__ . '/vendor/autoload.php';

// If this file exists in the composer autoload, then instance will successfully create.
new WPPunk\Autoload\AwesomeClass();

The composer autoload register the own callback use the spl_autoload.

PSR autoloading it’s a really good solution and you just need once add this to composer.json and forget about it. How to update your coding standard to the PSR-4 support?

But the classmap and files autoloading these not better solutions because you need after adding new files, again and again, dump composer autoload.

Custom autoloader with spl_autoload

Sometimes a project can be quite large and it is difficult to swap it into PSR. Faced with such a problem, I decided to make an autoload package that would work similarly to composer autoloading.

As a developer, I understand that if creating a new class will cause difficulty, then developers will not create new classes and will write huge classes.

I have also conducted a huge number of security and performance audits. One of the biggest performance issues is custom autoloading.

class Autoload {

	private $prefix;
	
	private $folder;

	public function __construct( $prefix, $folder, Cache $cache ) {
		$this->prefix = ltrim( $prefix, '\\' );
		$this->folder = $folder;
		spl_autoload_register( [ $this, 'autoload' ] );
	}

	public function autoload( $class ) {
		if ( 0 !== strpos( $class, $this->prefix ) ) {
			return;
		}

		require_once $this->file_path( $class );
	}

}

Use the function spl_autoload_register you can register your own callback with the autoload logic.

In the argument, you get the full class name for an undefined class.

The most important things here:

  • Checks the namespace as soon as possible because other plugins, themes can use your callback and this simple check save your site performance.
  • DON’T USE the file_exists function. What happens if your file will not found? The fatal error and it’s awesome, but if your code works…WTF? Then your code works with some magic. Magic in the code unstably. Don’t use the magic.

Then use the “pretty” code to transform namespace in the path.

private function file_path( $class ) {
	$class        = str_replace( $this->prefix, '', $class );
	$plugin_parts = explode( '\\', $class );
	$name         = array_pop( $plugin_parts );
	$name         = preg_match( '/^(Interface|Trait)/', $name )
		? $name . '.php'
		: 'class-' . $name . '.php';
	$local_path   = implode( '/', $plugin_parts ) . '/' . $name;
	$local_path   = strtolower( str_replace( [ '\\', '_' ], [ '/', '-' ], $local_path ) );

	$path = $this->folder . '/' . $local_path;

	return $path;
}

I know what this code a really ugly. If you have a better solution you’re welcome to comments.

Also, you can add the cache for a more quickly run.

Use the WordPress autoload via composer

I’ve created the package for packagist for WordPress autoload. And now you can use autoload as the composer package.

composer require wppunk/wpautoload

Then update your composer.json:

{
	...
	"extra": {
		"wp-autoload": {
			"\\Awesome\\Namespace\\": "src"
		}
	},
	...
}

Then in your main PHP-file include the vendor/autoload.php

<?php

require_once __DIR__ . '/vendor/autoload.php';

// If this file src/class-some-class.php exists then instance will successfully create.
new\Awesome\Namespace\SomeClass();

Also, this package exists on GitHub and you can contribute if have any ideas.

Leave a Reply

Your email address will not be published. Required fields are marked *