Skip to content

Repository files navigation

wpify/scoper

CI Packagist Downloads PHP License

A Composer plugin that moves your dependencies into a namespace of your own, so that your WordPress plugin or theme cannot collide with anybody else's.

The problem

WordPress loads every active plugin into one PHP process, and PHP has one global namespace. Your plugin requires guzzlehttp/guzzle 7. Another plugin on the same site bundles Guzzle 6. Whichever autoloader registers first wins, the other plugin gets a class it does not recognise, and something fatals — on a site you do not control and cannot test against.

PHP Scoper solves this by rewriting your dependencies under a prefix that nobody else will use. But it prefixes everything it can see, WordPress's own functions and classes included, so a scoped plugin ends up calling MyPlugin\Deps\add_action() — which does not exist.

This plugin is PHP Scoper wired up correctly for WordPress. It ships a generated database of every symbol declared by WordPress, WooCommerce, Action Scheduler and WP-CLI, and keeps those unprefixed while everything else moves into your namespace.

Requirements

PHP 8.2 or newer, and Composer 2.6 or newer.

Quickstart

Install the plugin globally, pinned to a major version:

composer global config --no-plugins allow-plugins.wpify/scoper true
composer global require wpify/scoper:^4.0

In your plugin, declare the dependencies you want scoped in composer-deps.json — same format as composer.json, but it holds only the dependencies that get prefixed:

{
  "require": {
    "guzzlehttp/guzzle": "^7.0"
  },
  "config": {
    "platform": {
      "php": "8.2.0"
    }
  }
}

Then tell the plugin what namespace to use, in your ordinary composer.json:

{
  "config": {
    "allow-plugins": {
      "wpify/scoper": true
    }
  },
  "extra": {
    "wpify-scoper": {
      "prefix": "MyPlugin\\Deps"
    }
  }
}

Run Composer:

composer install

The scoped tree is written to deps/. Load its autoloader alongside Composer's own:

<?php
require_once __DIR__ . '/deps/scoper-autoload.php';
require_once __DIR__ . '/vendor/autoload.php';

$client = new \MyPlugin\Deps\GuzzleHttp\Client();

That class is yours alone. The other plugin's Guzzle no longer matters.

Full walkthrough with the verification steps: Getting started.

Publishing to WordPress.org?

Write the scoped tree to vendor-prefixed/ instead. Since October 2024 every new plugin submitted to the WordPress.org directory is run through Plugin Check first, and an error blocks the submission until it is fixed. Plugin Check skips vendor-prefixed/ — it carries the name in its ignore list precisely so that prefixed dependencies do not raise false positives — but it does scan deps/, so a scoped Guzzle gets read as your code and reported for every file_get_contents() and curl_* call in it, none of which you can fix.

{
  "extra": {
    "wpify-scoper": {
      "prefix": "MyPlugin\\Deps",
      "folder": "vendor-prefixed"
    }
  }
}
require_once __DIR__ . '/vendor-prefixed/scoper-autoload.php';
require_once __DIR__ . '/vendor/autoload.php';

Add /vendor-prefixed/ to .gitignore, and make sure your release build does not strip it. The full picture — what the exemption does and does not cover, and the checklist to run before submitting — is in Publishing to WordPress.org.

Documentation

Getting started Install it and scope your first dependency, start to finish.
Configuration Every extra.wpify-scoper key, every command, every environment variable.
Troubleshooting Symptom, cause, fix. Start here when something is wrong.
Publishing to WordPress.org Why the scoped tree belongs in vendor-prefixed/, and the pre-submission checklist.
Deployment What to commit, CI recipes, Bedrock, multi-plugin repositories.
Customizing php-scoper scoper.custom.php and patchers, for dependencies that need help.
How it works The symbol lists, the pipeline, and why the scoped tree is swapped rather than written in place.
Upgrading to 4.0 Coming from 3.2 or earlier.

Contributing

Bug reports, symbol-list regressions and pull requests are welcome. See CONTRIBUTING.md — in particular for what sources/ is, why require-dev contains WordPress, and how to regenerate the symbol lists.

Security issues should not go in a public issue. See SECURITY.md.

License

GPL-2.0-or-later. Copyright © 2021–2026 WPify s.r.o. and contributors.

About

Composer plugin that scopes dependencies for WordPress plugins and themes, keeping WordPress and WooCommerce symbols unprefixed.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages