A tool to prefix namespaces and classnames in PHP files to avoid autoloading collisions.
A fork of Mozart. For Composer for PHP.
The primary use case is WordPress plugins, where different plugins active in a single WordPress install could each include different versions of the same library. The version of the class loaded would be whichever plugin registered the autoloader first, and all subsequent instantiations of the class will use that version, with potentially unpredictable behaviour and missing functionality.
Require as normal with Composer:
composer require --dev brianhenryie/strauss
and use vendor/bin/strauss
to execute.
Or, download strauss.phar
from releases,
curl -o strauss.phar -L -C - https://github.com/BrianHenryIE/strauss/releases/download/0.11.0/strauss.phar
Then run it from the root of your project folder using php strauss.phar
.
Its use should be automated in Composer scripts.
"scripts": {
"strauss": [
"vendor/bin/strauss"
],
"post-install-cmd": [
"@strauss"
],
"post-update-cmd": [
"@strauss"
]
}
or
"scripts": {
"strauss": [
"@php strauss.phar"
]
}
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": "strauss",
"namespace_prefix": "BrianHenryIE\\My_Project\\",
"classmap_prefix": "BrianHenryIE_My_Project_",
"constant_prefix": "BHMP_",
"packages": [
],
"override_autoload": {
},
"exclude_from_copy": {
"packages": [
],
"namespaces": [
],
"file_patterns": [
]
},
"exclude_from_prefix": {
"packages": [
],
"namespaces": [
],
"file_patterns": [
"/^psr.*$/"
]
},
"namespace_replacement_patterns" : {
},
"delete_vendor_files": false
}
},
The following configuration is inferred:
target_directory
defines the directory the files will be copied tonamespace_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 therequire
key of yourcomposer.json
are includedclassmap_output
is abool
to decide if Strauss will createautoload-classmap.php
andautoload.php
. If it is not set, it isfalse
iftarget_directory
is in your project'sautoload
key,true
otherwise.
The following configuration is default:
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.exclude_from_prefix
/file_patterns
:[/psr.*/]
PSR namespaces are ignored by default for interoperability. If you override this key, be sure to include/psr.*/
too.include_modified_date
is abool
to decide if Strauss should include a date in the (phpdoc) header written to modified files. Defaults totrue
.include_author
is abool
to decide if Strauss should include the author name in the (phpdoc) header written to modified files. Defaults totrue
.
The remainder is empty:
constant_prefix
is fordefine( "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_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 match
exclude_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 inpreg_replace
instead of prefixing withnamespace_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
to the classmap
and strauss will not create its own autoload.php
. psr-4
autoloading is not straightforward with Strauss's approach to copying files, so stick with Mozart for that.
"autoload": {
"classmap": [
"src",
"strauss"
]
},
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:
- A single output directory whose structure matches source vendor directory structure (conceptually easier than Mozart's independent
classmap_directory
anddep_directory
) - A generated
autoload.php
toinclude
in your project (analogous to Composer'svendor/autoload.php
) - Handles
files
autoloaders – and any autoloaders that Composer itself recognises, since Strauss uses Composer's own tooling to parse the packages - Zero configuration – Strauss infers sensible defaults from your
composer.json
- No destructive defaults –
delete_vendor_files
defaults tofalse
, so any destruction is explicitly opt-in - Licence files are included and PHP file headers are edited to adhere to licence requirements around modifications. My understanding is that re-distributing code that Mozart has handled is non-compliant with most open source licences – illegal!
- Extensively tested – PhpUnit tests have been written to validate that many of Mozart's bugs are not present in Strauss
- More configuration options – allowing exclusions in copying and editing files, and allowing specific/multiple namespace renaming
- Respects
composer.json
vendor-dir
configuration - Prefixes constants (
define
) - Handles meta-packages and virtual-packages
Strauss will read the Mozart configuration from your composer.json
to enable a seamless migration.
- Comprehensive attribution of code forked from Mozart – changes have been drastic and
git blame
is now useless, so I intend to add more attributions - More consistent naming. Are we prefixing or are we renaming?
- Further unit tests, particularly file-system related
- Regex patterns in config need to be validated
The 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.