Prefixes all PHP namespaces in a file or directory.

0.6.0 2018-02-27 23:35 UTC


Package version Travis Build Status AppVeyor Build Status Scrutinizer Code Quality Code Coverage Slack License

PHP-Scoper is a tool which essentially moves any body of code, including all dependencies such as vendor directories, to a new and distinct namespace.


PHP-Scoper's goal is to make sure that all code for a project lies in a distinct PHP namespace. This is necessary, for example, when building PHARs that:

  • Bundle their own vendor dependencies; and
  • Load/execute code from arbitrary PHP projects with similar dependencies

When a package (of possibly different versions) exists, and is found in both a PHAR and the executed code, the one from the PHAR will be used. This means these PHARs run the risk of raising conflicts between their bundled vendors and the vendors of the project they are interacting with, leading to issues that are potentially very difficult to debug due to dissimilar or unsupported package versions.

Table of Contents


PHAR (preferred)

The preferred method of installation is to use the PHP-Scoper PHAR, which can be downloaded from the most recent Github Release. Subsequent updates can be downloaded by running:

php-scoper.phar self-update

As the PHAR is signed, you should also download the matching php-scoper.phar.pubkey to the same location. If you rename php-scoper.phar to php-scoper, you should also rename php-scoper.phar.pubkey to php-scoper.pubkey.


You can install PHP-Scoper with Composer:

composer global require humbug/php-scoper

If you cannot install it because of a dependency conflict or you prefer to install it for your project, we recommend you to take a look at bamarni/composer-bin-plugin. Example:

composer require --dev bamarni/composer-bin-plugin
composer bin php-scoper require --dev humbug/php-scoper

Keep in mind however that this library is not designed to be extended.


php-scoper add-prefix

This will prefix all relevant namespaces in code found in the current working directory. The prefixed files will be accessible in a build folder. You can then use the prefixed code to build your PHAR.

Warning: After prefixing the files, if you are relying on Composer for the autoloading, dumping the autoloader again is required.

For a more concrete example, you can take a look at PHP-Scoper's build step in Makefile, especially if you are using Composer as there are steps both before and after running PHP-Scoper to consider.

Refer to TBD for an in-depth look at scoping and building a PHAR taken from PHP-Scoper's makefile.


If you need more granular configuration, you can create a by running the command php-scoper init. A different file/location can be passed with a --config option.

<?php declare(strict_types=1);


use Isolated\Symfony\Component\Finder\Finder;

return [
    'finders' => [],
    'patchers' => [],
    'global_namespace_whitelist' => [],
    'whitelist' => [],

Finders and paths

By default when running php-scoper add-prefix, it will prefix all relevant code found in the current working directory. You can however define which files should be scoped by using Finders in the configuration:

<?php declare(strict_types=1);


use Isolated\Symfony\Component\Finder\Finder;

return [
    'finders' => [

Besides the finder, you can also add any path via the command:

php-scoper add-prefix file1.php bin/file2.php

Paths added manually are appended to the paths found by the finders.


When scoping PHP files, there will be scenarios where some of the code being scoped indirectly references the original namespace. These will include, for example, strings or string manipulations. PHP-Scoper has limited support for prefixing such strings, so you may need to define patchers, one or more callables in a configuration file which can be used to replace some of the code being scoped.

Here's a simple example:

  • Class names in strings.

You can imagine instantiating a class from a variable which is based on a known namespace, but also on a variable classname which is selected at runtime. Perhaps code similar to:

$type = 'Foo'; // determined at runtime
$class = 'Humbug\\Format\\Type\\' . $type;

If we scoped the Humbug namespace to PhpScoperABC\Humbug, then the above snippet would fail as PHP-Scoper cannot interpret the above as being a namespaced class. To complete the scoping successfully, a) the problem must be located and b) the offending line replaced.

The patched code which would resolve this issue might be:

$type = 'Foo'; // determined at runtime
$scopedPrefix = array_shift(explode('\\', __NAMESPACE__));
$class = $scopedPrefix . '\\Humbug\\Format\\Type\\' . $type;

This and similar issues may arise after scoping, and can be debugged by running the scoped code and checking for issues. For this purpose, having a couple of end to end tests to validate post-scoped code or PHARs is recommended.

Applying such a change can be achieved by defining a suitable patcher in

<?php declare(strict_types=1);


return [
    'patchers' => [
        function (string $filePath, string $prefix, string $content): string {
            // PHP-Parser patch conditions for file targets
            if ($filePath === '/path/to/offending/file') {
                return preg_replace(
                    "%\$class = 'Humbug\\\\Format\\\\Type\\\\' . \$type;%",
                    '$class = \'' . $prefix . '\\\\Humbug\\\\Format\\\\Type\\\\\' . $type;',
            return $content;


PHP-Scoper's goal is to make sure that all code for a project lies in a distinct PHP namespace. However, you may want to share a common API between the bundled code of your PHAR and the consumer code. For example if you have a PHPUnit PHAR with isolated code, you still want the PHAR to be able to understand the PHPUnit\Framework\TestCase class.

A way to achieve this is by specifying a list of classes to not prefix:

<?php declare(strict_types=1);


return [
    'whitelist' => [

Note that only classes are whitelisted, this does not affect constants or functions.

For whitelist to work, you then require to load vendor/scoper-autoload.php instead of the traditional vendor/autoload.php.

Building A Scoped PHAR

This is a brief run through of the basic steps encoded in PHP-Scoper's own Makefile and elsewhere to build a PHAR from scoped code.

Step 1: Configure build location and prep vendors

If, for example, you are using Box to build your PHAR, you should set the base-path configuration option in your box.json file to point at the directory which will host scoped code. PHP-Scoper, by default, creates a build directory relative to the current working directory.

"base-path": "build"

Assuming you need no dev dependencies, run:

composer install --no-dev --prefer-dist

Step 2: Run PHP-Scoper

PHP-Scoper copies code to a new location during prefixing, leaving your original code untouched. The default location is ./build. You can change the default location using the --output-dir option. By default, it also generates a random prefix string. You can set a specific prefix string using the --prefix option. If automating builds, you can set the --force option to overwrite any code existing in the output directory without being asked to confirm.

Onto the basic command assuming default options from your project's root directory:

bin/php-scoper add-prefix

As there are no path arguments, the current working directory will be scoped to ./build in its entirety. Of course, actual prefixing is limited to PHP files, or PHP scripts. Other files are copied unchanged, though we also need to scope certain Composer related files.

Speaking of scoping Composer related files... The next step is to dump the Composer autoloader if we depend on it, so everything works as expected:

composer dump-autoload -d build --classmap-authoritative

Step 3: Build, test, and cleanup

If using Box, you can now move onto actually building the PHAR:

php -d phar.readonly=0 box build -vvv

At this point, it's best to have some simple end-to-end tests automated to put the PHAR through its paces and locate any problems (see Patchers and Whitelists from earlier in this README). Assuming it passes testing, the PHAR is ready.

Cleanup is simply to optionally delete ./build contents, and remember to re-install dev dependencies removed during Step 1:

composer install


PSR-0 support

With the following composer.json autoloading configuration:

    "autoload": {
        "psr-0": {
            "Foo": "src/"

If following PSR-0, with the expected file structure is:


However this also works:


This is unexpected as Foo is a file rather than a directory.

PHP-Scoper supports PSR-0 by transforming the configuration into a PSR-4 configuration. However support a case like above would require to scan the file structure which would add a significant overhead besides being more complex. As a result PHP-Scoper do not support the exotic case above.

String values

PHP-Scoper tries whenever possible to prefix strings as well:


// Will be prefixed into:


PHP-Scoper uses a regex to determine if the string is a class name that must be prefixed. But there is bound to have confusing cases. For example:

  • If you have a plain string 'Acme\Foo' which has nothing to do with a class, PHP-Parser will not be able to tell and will prefix it
  • Classes belonging to the global scope: 'Acme_Foo', because there is no way to know if it is a class name or a random string.

Native functions and constants shadowing

In the following example:


namespace Foo;


No use statement is used for the function is_array. This means that PHP will try to load the function \Foo\is_array and if fails to do so will fallback on \is_array (note that PHP does so only for functions and constants: not classes).

In order to bring some performance optimisation, the call will nonetheless be prefixed in \is_array. This will break your code if you were relying on \Foo\is_array instead. This however should be extremely rare, so if that happens you have two solutions: use a patcher or simpler remove any ambiguity by making use of a use statement (which is unneeded outside of the context of prefixing your code):


namespace Foo;

use function Foo\is_array;


The situation is exactly the same for constants.


PHP-Scoper does not support prefixing the dumped Composer autoloader and autoloading files. This is why you have to manually dump the autoloader again after prefixing an application.

Composer Plugins

Composer plugins are not supported. The issue is that for whitelisting symbols PHP-Scoper relies on the fact that you should load the vendor/scoper-autoload.php file instead of vendor/autoload.php to trigger the loading of the right classes with their class aliases. However Composer does not do that and as a result interfaces such as Composer\Plugin\Capability\Capable are prefixed but the alias is not registered.

This cannot be changed easily so for now when you are using an isolated version of Composer, you will need to use the --no-plugins option.


Contribution Guide


Project originally created by: Bernhard Schussek (@webmozart) which has now been moved under the Humbug umbrella.