composer / composer

Dependency Manager for PHP
https://getcomposer.org/
MIT License
28.67k stars 4.56k forks source link

Peer Dependencies? #5718

Closed i-am-tom closed 8 years ago

i-am-tom commented 8 years ago

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 :)

alcohol commented 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.

i-am-tom commented 8 years ago

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!

Seldaek commented 8 years ago

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?

Seldaek commented 8 years ago

Also note that it requires some illuminate stuff in ^4||^5, which is essentially the same as laravel replaces the illuminate packages.

i-am-tom commented 8 years ago

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-am-tom commented 8 years ago

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!

stof commented 8 years ago

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)

aaronjameslang commented 8 years ago

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?

stof commented 8 years ago

@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.

scil commented 6 years ago

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.

mindplay-dk commented 6 years ago

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.

stof commented 6 years ago

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.

fgm commented 6 years ago

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 ?

hypeJunction commented 5 years ago

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.

jchook commented 4 years ago

I ran into this exact issue today. Here's the npm peerDependency article for reference.

fgm commented 4 years ago

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.

alcohol commented 4 years ago

@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:

stof commented 4 years ago

@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)