A tool to prefix namespaces, classnames, and constants in PHP files to avoid autoloading collisions.
A fork of Mozart for Composer for PHP.
Have you ever activated a WordPress plugin that has a conflict with another because the plugins use two different versions of the same PHP library? Strauss is the solution to that problem - it ensures that your plugin's PHP dependencies are isolated and loaded from your plugin rather than loading from whichever plugin's autoloader registers & runs first.
⚠️ Sponsorship: I don't want your money. Please write a unit test to help the project.
.phar
file (recommended)composer dump-autoload
.phar
file (recommended)There are a couple of small steps to make this possible.
bin/.gitkeep
fileThis ensures that there is a bin/
directory in the root of your project. This is where the .phar
file will go.
mkdir bin
touch bin/.gitkeep
.gitignore
the .phar
fileAdd the following to your .gitignore
:
bin/strauss.phar
composer.json
`scriptsIn your composer.json
, add strauss
to the scripts
section:
"scripts": {
"prefix-namespaces": [
"sh -c 'test -f ./bin/strauss.phar || curl -o bin/strauss.phar -L -C - https://github.com/BrianHenryIE/strauss/releases/latest/download/strauss.phar'",
"@php bin/strauss.phar",
"@php composer dump-autoload"
],
"post-install-cmd": [
"@prefix-namespaces"
],
"post-update-cmd": [
"@prefix-namespaces"
]
}
This provides composer strauss
, which does the following:
sh -c
command tests if bin/strauss.phar
exists, and if not, downloads it from releases.@php bin/strauss.phar
is run to prefix the namespaces.If you prefer to include Strauss as a dev dependency, you can still do so. You mileage may vary when you include it this way.
composer require --dev brianhenryie/strauss
composer.json
`scripts"scripts": {
"prefix-namespaces": [
"strauss",
"@php composer dump-autoload"
],
"post-install-cmd": [
"@prefix-namespaces"
],
"post-update-cmd": [
"@prefix-namespaces"
]
}
If you add Strauss to your composer.json
as indicated in Installation, it will run when you composer install
or composer update
. To run Strauss directly, simply use:
composer prefix-namespaces
To update the files that call the prefixed classes, you can use --updateCallSites=true
which uses your autoload key, or --updateCallSites=includes,templates
to explicitly specify the files and directories.
composer -- prefix-namespaces --updateCallSites=true
or
composer -- prefix-namespaces --updateCallSites=includes,templates
Strauss potentially requires zero configuration, but likely you'll want to customize a little, by adding in your composer.json
an extra/strauss
object. The following is the default config, where the namespace_prefix
and classmap_prefix
are determined from your composer.json
's autoload
or name
key and packages
is determined from the require
key:
"extra": {
"strauss": {
"target_directory": "vendor-prefixed",
"namespace_prefix": "BrianHenryIE\\My_Project\\",
"classmap_prefix": "BrianHenryIE_My_Project_",
"constant_prefix": "BHMP_",
"packages": [
],
"update_call_sites": false,
"override_autoload": {
},
"exclude_from_copy": {
"packages": [
],
"namespaces": [
],
"file_patterns": [
]
},
"exclude_from_prefix": {
"packages": [
],
"namespaces": [
],
"file_patterns": [
]
},
"namespace_replacement_patterns" : {
},
"delete_vendor_packages": false,
"delete_vendor_files": false
}
},
The following configuration is inferred:
target_directory
defines the directory the files will be copied to, default vendor-prefixed
namespace_prefix
defines the default string to prefix each namespace withclassmap_prefix
defines the default string to prefix class names in the global namespacepackages
is the list of packages to process. If absent, all packages in the require
key of your composer.json
are includedclassmap_output
is a bool
to decide if Strauss will create autoload-classmap.php
and autoload.php
. If it is not set, it is false
if target_directory
is in your project's autoload
key, true
otherwise.The following configuration is default:
delete_vendor_packages
: false
a boolean flag to indicate if the packages' vendor directories should be deleted after being processed. It defaults to false, so any destructive change is opt-in.
delete_vendor_files
: false
a boolean flag to indicate if files copied from the packages' vendor directories should be deleted after being processed. It defaults to false, so any destructive change is opt-in. This is maybe deprecated! Is there any use to this that is more appropriate than delete_vendor_packages
?
include_modified_date
is a bool
to decide if Strauss should include a date in the (phpdoc) header written to modified files. Defaults to true
.
include_author
is a bool
to decide if Strauss should include the author name in the (phpdoc) header written to modified files. Defaults to true
.
update_call_sites
: false
. This can be true
, false
or an array
of directories/filepaths. When set to true
it defaults to the directories and files in the project's autoload
key. The PHP files and directories' PHP files will be updated where they call the prefixed classes.
The remainder is empty:
constant_prefix
is for define( "A_CONSTANT", value );
-> define( "MY_PREFIX_A_CONSTANT", value );
. If it is empty, constants are not prefixed (this may change to an inferred value).override_autoload
a dictionary, keyed with the package names, of autoload settings to replace those in the original packages' composer.json
autoload
property.exclude_from_prefix
/ file_patterns
exclude_from_copy
packages
array of package names to be skippednamespaces
array of namespaces to skip (exact match from the package autoload keys)file_patterns
array of regex patterns to check filenames against (including vendor relative path) where Strauss will skip that file if there is a matchexclude_from_prefix
packages
array of package names to exclude from prefixing.namespaces
array of exact match namespaces to exclude (i.e. not substring/parent namespaces)namespace_replacement_patterns
a dictionary to use in preg_replace
instead of prefixing with namespace_prefix
.Strauss uses Composer's own tools to generate a classmap file in the target_directory
and creates an autoload.php
alongside it, so in many projects autoloading is just a matter of:
require_once __DIR__ . '/strauss/autoload.php';
If you prefer to use Composer's autoloader, add your target_directory
(default vendor-prefixed
) to your autoload
classmap
and Strauss will not create its own autoload.php
when run. Then run composer dump-autoload
to include the newly copied and prefixed files in Composer's own classmap.
"autoload": {
"classmap": [
"vendor-prefixed/"
]
},
I was happy to make PRs to Mozart to fix bugs, but they weren't being reviewed and merged. At the time of writing, somewhere approaching 50% of Mozart's code was written by me with an additional nine open PRs and the majority of issues' solutions provided by me. This fork is a means to merge all outstanding bugfixes I've written and make some more drastic changes I see as a better approach to the problem.
Benefits over Mozart:
classmap_directory
and dep_directory
)autoload.php
to include
in your project (analogous to Composer's vendor/autoload.php
)files
autoloaders – and any autoloaders that Composer itself recognises, since Strauss uses Composer's own tooling to parse the packagescomposer.json
delete_vendor_files
defaults to false
, so any destruction is explicitly opt-incomposer.json
vendor-dir
configurationdefine
)Strauss will read the Mozart configuration from your composer.json
to enable a seamless migration.
I don't have a strong opinion on these. I began using Mozart because it was easy, then I adapted it to what I felt was most natural. I've never used these.
psr/*
packages no longer excluded by defaulttarget_directory
changes from strauss
to vendor-prefixed
Please open issues to suggest possible breaking changes. I think we can probably move to 1.0.0 soon.
git blame
is now useless, so I intend to add more attributionsThe correct approach to this problem is probably via PHP-Parser. At least all the tests will be useful.
Coen Jacobs and all the contributors to Mozart, particularly those who wrote nice issues.