In PHP, with Composer, your dependencies are flattened, which might result in conflicts. Most of the time those conflicts are legitimate and should be properly resolved. However you may have dev tools that you want to manage via Composer for convenience, but should not influence your project dependencies or for which conflicts don't make sense. For example: EtsyPhan and PhpMetrics. Installing one of those static analysis tools should not change your application dependencies, neither should it be a problem to install both of them at the same time.
It allows you to install your bin vendors in isolated locations, and still link them to your bin-dir (if you want to).
This is done by registering a bin
command, which can be used to run Composer commands inside a namespace.
# Globally
$ composer global require bamarni/composer-bin-plugin
# In your project
$ composer require --dev bamarni/composer-bin-plugin
$ composer bin [namespace] [composer_command]
$ composer global bin [namespace] [composer_command]
Let's install Behat and PhpSpec inside a bdd
bin namespace, EtsyPhan in etsy-phan
and PhpMetrics
in phpmetrics
:
$ composer bin bdd require behat/behat phpspec/phpspec
$ composer bin etsy-phan require etsy/phan
$ composer bin phpmetrics require phpmetrics/phpmetrics
This command creates the following directory structure :
.
├── composer.json
├── composer.lock
├── vendor/
│ └── bin
│ ├── behat -> ../../vendor-bin/bdd/vendor/behat/behat/bin/behat
│ ├── phpspec -> ../../vendor-bin/bdd/vendor/phpspec/phpspec/bin/phpspec
│ ├── phan -> ../../vendor-bin/etsy-phan/vendor/etsy/phan/phan
│ └── phpmetrics -> ../../vendor-bin/phpmetrics/vendor/phpmetrics/phpmetrics/bin/phpmetrics
└── vendor-bin/
└── bdd
│ ├── composer.json
│ ├── composer.lock
│ └── vendor/
│ ├── behat/
│ ├── phpspec/
│ └── ...
└── etsy-phan
│ ├── composer.json
│ ├── composer.lock
│ └── vendor/
│ ├── etsy/
│ └── ...
└── phpmetrics
├── composer.json
├── composer.lock
└── vendor/
├── phpmetrics/
└── ...
You can continue to run vendor/bin/behat
, vendor/bin/phpspec
and co. as before but they will be properly isolated.
Also, composer.json
and composer.lock
files in each namespace will allow you to take advantage of automated dependency
management as normally provided by Composer.
The all
bin namespace has a special meaning. It runs a command for all existing bin namespaces. For instance, the
following command would update all your bins :
$ composer bin all update
Changed current directory to vendor-bin/phpspec
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
Changed current directory to vendor-bin/phpunit
Loading composer repositories with package information
Updating dependencies (including require-dev)
Nothing to install or update
Generating autoload files
If we take the case described in the example section, there might be more binaries linked due to
the dependencies. For example PhpMetrics depends on Nikic PHP-Parser and as such you will also have php-parse
in .vendor/bin/
:
.
├── composer.json
├── composer.lock
├── vendor/
│ └── bin
│ ├── phpmetrics -> ../../vendor-bin/phpmetrics/vendor/phpmetrics/phpmetrics/bin/phpmetrics
│ └── php-parse -> ../../vendor-bin/phpmetrics/vendor/nikic/PHP-Parser/bin/php-parsee
└── vendor-bin/
└── phpmetrics
├── composer.json
├── composer.lock
└── vendor/
├── phpmetrics/
├── nikic/
└── ...
But what happens if another bin-namespace has a dependency using Nikic PHP-Parser? In that situation symlinks would collides and are not created (only the colliding ones).
For convenience, you can add the following script in your composer.json
:
{
"scripts": {
"bin": "echo 'bin not installed'",
"post-install-cmd": ["@composer bin all install --ansi"],
"post-update-cmd": ["@composer bin all update --ansi"]
}
}
This makes sure all your bins are installed during composer install
and updated during composer update
.
By default, binaries of the sub namespaces are linked to the root one like described in example. If you wish to disable that behaviour, you can do so by adding a little setting in the extra config:
{
"extra": {
"bamarni-bin": {
"bin-links": false
}
}
}
By default, the packages are looked for in the vendor-bin
directory. The location can be changed using target-directory
value in the extra config:
{
"extra": {
"bamarni-bin": {
"target-directory": "ci/vendor"
}
}
}
You can add following line to your .gitignore
file in order to avoid committing dependencies of your tools.
/vendor-bin/**/vendor
Updating each tool can create many not legible changes in composer.lock
files. You can use .gitattributes
file in order
to inform git that it shouldn't show diffs of composer.lock
files.
vendor-bin/**/composer.lock binary
- theofidry/composer-inheritance-plugin: Opinionated version of Wikimedia composer-merge-plugin to work in pair with this plugin.