Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Optional dependencies #11635

Open
derrabus opened this issue Sep 12, 2023 · 4 comments
Open

[RFC] Optional dependencies #11635

derrabus opened this issue Sep 12, 2023 · 4 comments
Labels
Milestone

Comments

@derrabus
Copy link
Contributor

derrabus commented Sep 12, 2023

Motivation

A common problem for packages is dealing with dependencies that may or may not be present.

Take for example Symfony's Form component symfony/form. The main purpose of that component is handling HTML forms: Mapping structured data to a rendered HTML form and mapping an HTML form back to a PHP object. A second component, Validator (symfony/validator) can validate arbitrary objects against a set of configured constraints.

If you combine both components in compatible versions, you get form validation. ✌🏻 However, making the validator a mandatory dependency of the form component would mean that the validator gets installed even if a project just wants the form handling, e.g. because they use custom validation logic.

The current way to handle such a dependency would be to add the validator to the require-dev section of the form component, so it gets installed for development/testing, and add a conflict rule to prevent the installation of incompatible validator versions in downstream projects. The version constraint of the conflict rule would be pretty much the inverse of the versions constraint listed in require-dev.

Sounds complicated? Well, it is, and keeping both constraints in sync is a pain.

Proposal

I'd like to propose a third type of dependecy: optional dependencies, listed under require-optional with the same structure as require and require-dev. Listing a package in require-optional tells Composer that if the given package is going to be installed, it should allow the given version range only. For the root project, require-optional is merged with require-dev, which means an optional dependency is automatically a development dependency.

Example

{
    "require-optional": {
        "acme/foo": "^2.5 || ^3.1"
    }
}

Composer would treat this configuration as equivalent to the following

{
    "require-dev": {
        "acme/foo": "^2.5 || ^3.1"
    },
    "conflict": {
        "acme/foo": "< 2.5 || >= 3, < 3.1 || >= 4"
    }
}

Implementation

This feature is syntactic sugar. Because require-optional entries can be translated to conflict rules, my hope is that the resolver itself does not have to be changed.

Backwards compatibility

If this feature is accepted, older Composer versions would not interpret a composer.json file correctly that makes use of require-optional. However, when consuming such a package via Packagist, the Packagist server could act as a BC proxy by merging all require-optional entries into require-dev and conflict.

@Seldaek
Copy link
Member

Seldaek commented Sep 13, 2023

After discussing this a bit I think something like this would be better:

{
    "compatible": {
        "acme/foo": "^2.5 || ^3.1"
    }
}

That'd be converted to a conflict internally, but nothing more. No implicit require-dev, and wording is also hopefully less confusing than require-optional which are two words with kinda opposite meaning.

I think it might be workable, but adding a new top level key for such constraints if we want to do it nicely in terms of error reporting would be quite a lot of effort on the packagist.org side + composer as well I think. If we simply convert them to conflicts in packagist.org on import it'd be easier but probably more confusing when you get errors.

@Seldaek Seldaek added this to the Nice To Have milestone Sep 13, 2023
@derrabus
Copy link
Contributor Author

require-optional which are two words with kinda opposite meaning

🙈 You're right. I like compatible as well. 👍🏻

That'd be converted to a conflict internally, but nothing more. No implicit require-dev

Please reconsider that part. My motivation behind the proposal is that keeping conflict rules and require-dev in sync is painful at the moment. If we introduce that compatible key this way, I'd still have to keep compatible and require-dev in sync, which effectively means I copy everything that's in compatible into require-dev.

My life would be slightly easier though because I won't have to invert version constraints anymore.

@stof
Copy link
Contributor

stof commented Sep 13, 2023

@derrabus you could use * in the require-dev key then

@dennisverspuij
Copy link

This proposal is exactly what I described in #11610 a few issues earlier.
Current way of managing this with require-dev and conflict constraints is not only complicated, but it is also very opaque to users, it does not explain why these constraints are there. Having a new section optional hints towards that you may want that library for (support for) complementary functionality. And composer suggest should per haps list these as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants