Skip to content

PHP Namespace scope

PHP namespaces allow you to group related classes, interfaces, and functions under a common name to avoid naming conflicts with other PHP libraries. In WordPress, however, the recommended approach has been to add prefixes directly on the the class or the function name.

For example:

class AwesomePlugin_Portfolio {
}
function awesomeplugin_get_portfolio() {
}

This was necessary because, when WordPress started, PHP namespaces weren’t available (it was only introduced in PHP 5.3), and Composer didn’t exist yet.

Now that WordPress has updated its minimum requirement to PHP 7.4, it’s time to move away from prefixing and encourage modern PHP practices.

Challenges

Even with PHP namespaces, there are still challenges, especially when building plugins for WordPress. If your plugin includes a dependency and another plugin includes the same dependency, you can run into conflicts. This happens because classes or functions with the same name can’t coexist within the same namespace, causing PHP to complain that the class or function has already been declared.

To address this issue, this boilerplate includes a library named PHP-Scoper.

PHP-Scoper

PHP-Scoper is a tool that prefixes PHP namespaces in a file or directory. In this boilerplate, PHP-Scoper comes pre-configured with a command to add the prefix only to the dependencies listed in the require section in composer.json. Other dependencies listed in the require-dev section as well as other PHP files in the inc and src directory will remain as is.

Configuring PHP-Scoper

If you’ve decided on a namespace prefix, you can change the prefix configuration in the scoper.inc.php file:

scoper.inc.php
<?php
declare(strict_types=1);
use Isolated\Symfony\Component\Finder\Finder;
return [
/**
* The prefix configuration. If a non-null value is used, a random prefix
* will be generated instead.
*/
'prefix' => 'WPStarterPlugin\\Vendor',
'prefix' => 'JohnDoe\\AwesomePlugin\\Vendor',
/**
* List of symbols to consider internal i.e. to leave untouched.
*
* @see https://github.com/humbug/php-scoper/blob/master/docs/configuration.md#excluded-symbols
*/
'exclude-namespaces' => ['WPStarterPlugin'],
'exclude-namespaces' => ['JohnDoe\AwesomePlugin'],

…as well as in the namespace in autoload.psr-4 section in theh composer.json function:

composer.json
{
"name": "syntatis/wp-starter-plugin",
"description": "A starting point for your next plugin project",
"type": "wordpress-plugin",
"autoload": {
"psr-4": {
"WPStarterPlugin\\": "app/",
"JohnDoe\\AwesomePlugin\\": "app/"
}
}
}

Then, you can run the following command to apply the prefix:

Terminal window
composer scope

This command will scan the vendor directories, add the prefix to the PHP files, and move them to the dist-autoload directory. The original files will remain untouched. If you want to use the classes or the functions from the dependencies, you should now use the prefixed namespace.

app/Portfolio.php
<?php
namespace WPStarterPlugin\PostTypes;
use JohnDoe\AwesomePlugin\Vendor\Syntatis\WPHook\Hook;
use JohnDoe\AwesomePlugin\Vendor\Syntatis\WPHook\Contract\WithHook;
class Portfolio extends WithHook
{
public function hook(Hook $hook): void
{
$hook->addAction('init', [$this, 'registerPostType']);
}
public function registerPostType(): void
{
// Register custom post type.
// See: https://developer.wp.org/reference/functions/register_post_type/
}
}

If you update the dependencies or add a new one, you will run the composer scope command again to apply the prefix to the new files.

PHP-Scoper comes with a vast array of configuration and options. If you’d like to learn more or need to customize it further, the best place to get started is to refer the PHP-Scoper documentation.

Further Reading