Modern WordPress plugin development comes down to three habits. Put your code in a PHP namespace, load your classes with PSR-4 autoloading instead of a pile of require calls, and check every change against the WordPress Coding Standards. This guide shows how to set up all three, using the real configuration from WP License Manager, an open-source licensing plugin we build and maintain.
Why use a PHP namespace in a WordPress plugin?
Every active plugin shares one PHP process with WordPress core, the theme and every other plugin. If two of them declare a class called Settings or a function called get_options(), PHP stops with a fatal error. The old fix was to prefix every name by hand (myplugin_get_options()). A namespace does the same job for a whole file in one line, and it keeps class names short and readable inside your own code.
How should you name a plugin namespace?
Use one short, unique root for the plugin, then one sub-namespace per area of the code that mirrors your folder structure. In WP License Manager the root is WPLM, and the code is split into namespaces such as WPLM\Admin, WPLM\Licensing, WPLM\Crypto and WPLM\Install. Each one maps to a folder under src/, so WPLM\Licensing\CheckInService lives in src/Licensing/CheckInService.php.
A file starts with its namespace, the classes it uses, and the usual direct-access guard:
<?php
namespace WPLM\Licensing;
use WP_Error;
use WPLM\Models\License;
use WPLM\Repositories\LicenseRepository;
defined( 'ABSPATH' ) || exit;How do you set up PSR-4 autoloading in a WordPress plugin?
PSR-4 maps a namespace prefix to a folder, so PHP can find any class file on its own. With Composer, it takes one block in composer.json:
{
"autoload": {
"psr-4": {
"WPLM\\": "src/"
}
}
}Run composer dump-autoload, then load the generated autoloader from your main plugin file. WP License Manager also registers a small fallback autoloader, so the plugin still works if the vendor folder isn’t shipped:
// Autoloader — prefer Composer; fall back to a simple PSR-4 map.
if ( file_exists( WPLM_PLUGIN_DIR . 'vendor/autoload.php' ) ) {
require_once WPLM_PLUGIN_DIR . 'vendor/autoload.php';
} else {
spl_autoload_register(
function ( string $class ) {
if ( strpos( $class, 'WPLM\\' ) !== 0 ) {
return;
}
$relative = substr( $class, strlen( 'WPLM\\' ) );
$file = WPLM_PLUGIN_DIR . 'src/' . str_replace( '\\', '/', $relative ) . '.php';
if ( file_exists( $file ) ) {
require_once $file;
}
}
);
}Name each file after its class (CheckInService.php), not with the older WordPress class-check-in-service.php pattern. PSR-4 expects the file name to match the class name exactly.
How do hooks and WordPress functions work inside a namespace?
Two rules cover almost every case.
- Functions and constants fall back to the global namespace. If PHP can’t find
add_action()in your namespace, it uses the global one, so WordPress functions work unchanged. - Classes do not fall back. Inside
namespace WPLM\Licensing, a bareWP_ErrormeansWPLM\Licensing\WP_Error, which doesn’t exist. Import it withuse WP_Error;or write\WP_Error.
When you pass a class method as a hook callback, give the fully qualified class name as a string, because WordPress calls it from outside your namespace:
register_activation_hook( __FILE__, array( 'WPLM\\Plugin', 'activate' ) );
add_action( 'plugins_loaded', array( 'WPLM\\Plugin', 'get_instance' ) );Which WordPress coding standards should a plugin follow?
Use the official WordPress Coding Standards (WPCS) for PHP_CodeSniffer. Install them as development dependencies:
composer require --dev wp-coding-standards/wpcs:"^3.0"Composer may ask whether to allow the dealerdirect/phpcodesniffer-composer-installer plugin. Answer yes: it registers the WordPress standards with PHP_CodeSniffer so phpcs can find them.
Then add a phpcs.xml ruleset to the plugin. WP License Manager uses WordPress-Core for formatting and core security rules, plus WordPress-Extra for best-practice sniffs such as escaping, nonces and database calls. It switches off one sniff, the file-name rule, because PSR-4 file names don’t follow the older class-*.php convention:
<?xml version="1.0"?>
<ruleset name="WPLM">
<file>.</file>
<exclude-pattern>*/vendor/*</exclude-pattern>
<arg name="extensions" value="php"/>
<rule ref="WordPress-Core"/>
<rule ref="WordPress-Extra">
<exclude name="WordPress.Files.FileName"/>
</rule>
</ruleset>Add two Composer scripts so checking and fixing take one command each:
"scripts": {
"lint": "phpcs --standard=phpcs.xml .",
"lint-fix": "phpcbf --standard=phpcs.xml ."
}composer lint reports problems, and composer lint-fix fixes the formatting ones automatically. Run it before every commit, or in your CI pipeline, so standards never drift.
Checklist: a modern WordPress plugin setup
- One unique root namespace, with sub-namespaces that mirror the folders in
src/ - PSR-4 autoloading in
composer.json, with class-named files defined( 'ABSPATH' ) || exit;at the top of every PHP fileusestatements for WordPress classes such asWP_Error- Fully qualified class names in hook callbacks
- WPCS 3 with
WordPress-CoreandWordPress-Extra, run on every change
Need a plugin built this way?
We build custom WordPress and WooCommerce plugins with this setup, and we ship our own: NDV Image Upload is listed on WordPress.org, and WP License Manager is open source on GitHub. See our plugin development service or tell us what you need.