Closed i-am-tom closed 8 years ago
Could you give a more specific example? Because what you describe is already possible, as far as I understand your description. A can specify which versions of B it requires, and B can specify which version of A it requires (or the opposite, which versions it conflicts with). In general though, circular dependencies are bad and should be avoided.
Yes, of course:
I did a quick Packagist search, and found this package, which is designed to work with Laravel 4 or 5. It has no dependency on Laravel - which makes sense - but it therefore wouldn't work with a version other than 4 or 5. Similarly, Laravel has no dependency on this package, so the two packages are totally unconnected through Composer. This means that I can install Laravel 3 and this package, and nothing will complain until the run-time explosion.
However, if backup-manager/laravel
had a peer dependency on Laravel 4 or 5, I'm imagining that Composer could spot that, say, Laravel 3 was installed, and warn the user that backup-manager/laravel
wasn't designed to work with anything but 4 or 5.
By extension, if you had multiple packages installed - some for Laravel 3 only, some for Laravel 4 only - Composer could tell the user that no version of Laravel could be found that met the requirements of its peer dependents.
I'm sorry if that's a messy explanation - the link in my original post does a far better job of explaining this. TL;DR, it's an extra opportunity for constraints that makes it easier to use plugins!
That example package seems to me like it is entirely geared towards laravel integration, so why would it not require laravel/framework
^4.0 || ^5.0
?
Also note that it requires some illuminate stuff in ^4||^5
, which is essentially the same as laravel replaces the illuminate packages.
I suppose the reasoning is that dependencies imply a parent-child structure (at least, in my mind?), which isn't the same as the "sibling" structure that peer dependencies convey. Sorry, it was the first example that I came across - not necessarily the best!
I do see what you mean, though: the nature of PHP means that "add-on" packages usually implement an interface defined in the core package, which makes the core package a dependency, and so peers are probably unnecessary complexity. Perhaps I've spent too much time with JavaScript 👀 ...
I'll close the thread and bung everything under the dependencies key :) Thanks!
another thing is that npm separates the dependencies of a package from other dependencies (you really have a tree, and a package cannot access a transitive dependency). This is why peer dependencies make sense (it requires that the dependency is available higher in the tree). There is no such thing in composer (or in bundler AFAIK)
Perhaps I don't understand the concept, but it seems like composer only supports peer dependencies, rather than the sub-dependencies that are usual in npm?
@aaronjameslang sub-dependencies cannot exist in PHP, as class and function are global in PHP, unlike in CommonJS modules.
but Composer dependencies are not the same than npm peer dependencies either. A package will not trigger the installation of its peer dependency in npm. But it will complain if no package higher in the tree provided this peer dependency as part of their dependency.
I think Peer Dependencies is usefull. I'm dev LaravelFly which requires ext-swoole. When Dev users require it on windows os, they may failed because swoole requires linux, cywin or windows WSL.
I came here in search of something like peer dependencies.
My use-case is, we maintain a package that bootstraps a DI container with a bunch of things - a router, mail service, template engine etc. all from different packages.
This package is really solely bootstrapping - we currently just have all the component packages listed as required, but the problem is, people who install this package don't think to actually add dependencies on the packages they end up using, because it's php and they don't technically need to.
It's wrong not to do so though - since you're now dependent on some version of the router package, you could silently inherit a breaking change to that package.
The way I understood "peer", is it's saying "since you installed this package, you're required to also install this other package" - it lets them know they're going to have a dependency on certain packages and forces them to correctly define their dependencies rather than inherit them.
I know there's "suggest", and this is very much like that, except it's not a suggestion - but at the same time, it's not like "require" either, because you can't safely inherit these dependencies, as they're going to become your dependencies once you obtain them via DI etc.
Peer dependencies are useful in npm due to the way node resolves modules with its nested node_modules
folders (where peer dependencies must not appear in the node_modules
of the current module, but in the one of the parent).
In composer, you have a single vendor folder, so this does not make much sense (and for instance, bundler does not have this concept either, for the same reason).
@scil peer dependencies would not solve this case at all. What you want is considering a dependency as optional, which is not what peer dependencies are.
Considering the case of a Drupal 8 module providing Drush 9 commands, which are entirelty incompatible with Drush 8, but which can be used without Drush as a pure module. Does this mean that the correct declaration for this is not something like a "suggest" or the non-existing peer dependencies, but rather something like { "conflict": { "drupal/drush": "< 9.0" } }
instead ?
In other words, not recommending 9.x but forbidding lower versions ?
I think a better use case for peer dependencies is packages that use composer/installers
. Let's say you are building a plugin for a framework, you want to specify the version of the framework this plugin is designed for, but you don't want to install the entire framework as a dependency, in case you are also a building a dist package of your plugin.
I ran into this exact issue today. Here's the npm peerDependency article for reference.
A slightly different case is ankitpokhrel/tus-php
which bring in predis/predis
because users may want to user Redis for storage when using that package.
However, some dependencies like vimeo/vimeo-api
use tus, but have no need for Redis.
If there was a way for tus to specify "if you want to use predis/predis, then use these versions, otherwise don't care", like peerDependencies do, it would avoid bringing in predis on sites which have no use for redis, like most Vimeo API users.
@fgm The concept/purpose of peerDependencies
is slightly different though. I understand the use-case here however; and I also understand that the suggests
option is not adequate in this regard. On a side note; a potential work-around for your exact scenario would be to specify predis/predis
as a platform package, that way Composer will interpret it as already being installed.
Edit: forgot that only works for ext-*
packages :disappointed:
@fgm use a conflicts
rule to forbid incompatible versions (and suggests
if relevant to advocate for the optional dependency).
npm peerDependencies are a separate concept, which is not about optional dependencies but about sharing a dependency with the parent in the dependency tree (which is not relevant in PHP as PHP does not have a built-in concept of packages and class names are all global)
I've noticed that Composer doesn't have support for peer dependencies, and have just started on a chunk of work that could really benefit from them.
In general, the use case would be for "plugins" to larger packages (e.g. frameworks), where the plugin (A) has only been tested for a particular version of the larger package (B). A could then specify this version constraint for B that could be checked upon install.
Has this already been discussed (I couldn't find anything with a quick search)? Either way, if I put together a PR, is this something that would be of interest?
Thanks :)