humbug/php-scoper
PHP-Scoper prefixes your project and its dependencies into a unique namespace to avoid conflicts, especially when building PHARs that bundle vendor code and run alongside other PHP projects with overlapping packages or versions.
Installation:
composer require --dev humbug/php-scoper
Or globally:
composer global require humbug/php-scoper
First Use Case:
vendor/bin/php-scoper add-prefix
build/ directory with prefixed namespaces (default: ScopingPrefix\).Where to Look First:
php-scoper.json (auto-generated or manually created).vendor/bin/php-scoper inspect path/to/file.php to test changes.PHAR Integration:
php-scoper to prefix Laravel code + dependencies before building a PHAR (e.g., for CLI tools or standalone scripts).composer install --no-dev --prefer-dist
vendor/bin/php-scoper add-prefix --output-dir=build --prefix=App\\Scoped\
composer dump-autoload --working-dir=build --classmap-authoritative
Isolated Testing:
build/ before PHAR creation:
php build/artisan tinker # Test Laravel commands
CI/CD Automation:
composer.json scripts:
"scripts": {
"scope": "php-scoper add-prefix --output-dir=build --prefix=App\\Scoped\\",
"scope:test": "composer scope && composer dump-autoload --working-dir=build && php build/artisan test"
}
composer scope:test
Laravel-Specific Adjustments:
php-scoper.json:
"exclude": {
"namespaces": ["Illuminate\\", "Laravel\\"]
}
expose to avoid breaking facade calls:
"expose": {
"namespaces": ["App\\Providers\\RouteServiceProvider"],
"classes": ["App\\Http\\Kernel"]
}
Patchers for Laravel:
config_path()) via patchers:
// config/php-scoper.php
return [
'patchers' => [
'App\\Patcher\\ConfigPathPatcher',
],
];
Autoloader Conflicts:
composer dump-autoload in the build/ directory after scoping.--classmap-authoritative to avoid missing classes.Dynamic Symbols:
config(), app(), and route() helpers may break. Use patchers or exclude them:
"exclude": {
"symbols": ["config", "app", "route"]
}
Service Providers:
php build/artisan package:discover
Heredoc/Nowdoc:
<<<HTML\n<?php namespace Foo;) will fail. Use patchers or exclude files.Composer Plugins:
composer install --no-plugins
Inspect Files:
vendor/bin/php-scoper inspect app/Http/Controllers/UserController.php
Check Exposed Symbols:
build/vendor/composer/autoload_namespaces.php.Laravel-Specific Debugging:
"expose": {
"classes": ["App\\Providers\\AppServiceProvider"]
}
PHAR Testing:
$phar = new Phar('app.phar');
$phar->extractTo('phar-test');
Custom Patchers:
config_path()):
// src/Patcher/ConfigPathPatcher.php
namespace App\Patcher;
use Humbug\PhpScoper\Patcher\PatcherInterface;
class ConfigPathPatcher implements PatcherInterface { ... }
php-scoper.json:
"patchers": ["App\\Patcher\\ConfigPathPatcher"]
Exclusion Rules:
bootstrap/ or vendor/ selectively:
"exclude": {
"paths": ["bootstrap/cache/*", "vendor/laravel/*"]
}
Prefix Strategy:
App\\Scoped\\{branch-name}\\) in CI:
vendor/bin/php-scoper add-prefix --prefix="App\\Scoped\\${CI_COMMIT_REF_NAME}\\"
Facades:
App\\Scoped\\Http\\Kernel) won’t work unless exposed. Use aliases in config/app.php:
'aliases' => [
'App' => App\Scoped\Facades\App::class,
],
Service Container:
$this->app->bind('App\\Scoped\\Http\\Kernel', function () {
return new \App\Scoped\Http\Kernel(app());
});
Artisan Commands:
Artisan::command().Testing:
$this->partialMockBuilder(\App\Scoped\SomeClass::class)
->disableOriginalConstructor();
How can I help you explore Laravel packages today?