Matthias Noback has a new post today responding to a recent post talking about virtual packages with Composer (using "provide") and some of his own thoughts of how it relates to dependency inversion.
This is a response to Peter Petermann's article Composer and virtual packages. First, let's make this totally clear: I don't want to start an Internet war about this, I'm just pointing out some design issues that may arise from using Composer's provide option in your package's composer.json file. [...] Yes, if a user wants to run the code in your library, they need to have some class that implements [the "provides" requirement]. But no, this shouldn't be reflected in the dependencies of the library. Let me explain this by taking a look at the Dependency inversion principle.
He gives an example of using a specific package for logging (the Zend logger) and how that hard-coded dependency can be refactored out using one of two methods: either a custom interface or one described elsewhere. Getting back to "provide", he lists some reasons why he thinks that defining the interface itself in the Composer configuration is a good idea. These include:
- Strictly speaking (as in, would the code compile), the code from the library itself [...] just needs the LoggerInterface (which happens to be in the psr/log package).
- By depending on an implementation package, you basically undo any effort you made to depend on abstractions and not on concretions.
- Some day, someone may decide to introduce another virtual package, called the-real-psr/log-implementation.
- The notion of an "implementation package" is really vague. What does it mean for a package to be an implementation package.
Each of the reasons has a bit of description to go along with it. He also points out an interesting example where the package actually knows about existing virtual package, the DoctrinePHPCRBundle and its use of "jackalope" and "phpcr".