This article tries to elaborate and explain the simple, yet very important property in composer.json file -- the replace property.
The official explanation in Composer website is very brief and doesn't really explains the use of it that much.
Lets try to break it down into its details so we can see the bigger picture.The official documentation says:
Lists packages that are replaced by this package. This allows you to fork a package, publish it under a different name with its own version numbers, while packages requiring the original package continue to work with your fork because it replaces the original package.
Note the word "this". That means when it is defined in your library, that package replaces whatever package you specify it on. This could be useful if you have forked a library and you want to use your fork always. To better understand this. Lets take an example
- I have an application called "My App"
- "My App" requires two packages as dependency, namely "original/library" and "other/package"
- "other/package" has dependency on "original/library"
- The maintainer of "original/library" is not updating it anymore, and there is a lot of bugs on it
- You want to fix the issues on "original/library" but they are not accepting your Pull Requests
- You forked the "original/library" instead, in a package called "yourfork/library"
- You specify in your "My App " project to use "yourfork/library"
This is illustrated in the diagram below
Now, the problem is, the "other/package" has dependency on "original/library". Which results into having both copies loaded in your app! This is not good, since the buggy package ("original/library") is still loaded and can wreak havoc in your application.
There has something to be done, so that whenever there is a need to use "original/library", Composer knows that it should "replace it" by your fork, which is "yourfork/library".
Enter the replace propery
To fix this, you need to specify replace property in "yourfork/library", so that Composer knows that it can serve as a replacement for "original/library", whenever that package is being required.
Now. even though you haven't "told" directly the "other/package" to stop using the "original/library", Composer will not load the "original/library" anymore, since you told him that it can be replaced by "yourfork/library".
Does that makes sense?
The other angle:
This is also useful for packages that contain sub-packages, for example the main symfony/symfony package contains all the Symfony Components which are also available as individual packages. If you require the main package it will automatically fulfill any requirement of one of the individual components, since it replaces them.
- I have an application called "My App"
- "vendor/framework" is a cool but heavy framework with different components under it
- You can require the entire "vendor/framework" in your project, or just the individual components you prefer
- At initial stage, "My App" only required the "vendor/component1", since lets say the project only requires only that feature
- later on, you decided to use the full framework
- Now, your "My App" requires two packages as dependency namely "vendor/framework" and "vendor/component1"
- But "vendor/component1" classes is already present on "vendor/framework"
- This seems a redundant code and prone to conflicts!
The situation is depicted in the diagram below:
At first thought, you may think you can just simply remove the depedency of my "My App" to "vendor/component1". But the thing is, most of the time, users don't know which component is already present to which package!
Even if you knew, there are tons of dependency and relations of each package that it would be a nightmare to trace that by hand.
So there's got to be way for Composer to know, so that when a "vendor/component1" is already "included" in "vendor/framework" package, it should tell it that there is no need for the "vendor/component1" seperately anymore.
This can be achieved by putting a replace property in the "vendor/framework"
There are apparently unwanted side-effects of this behaviour though. Because if you require a package that does not exist, and a replacer replaces it with their own fork (which has malicious codes on it) -- you are doomed.
The explanation of the issue is beyond the scope of this article, but it is a MUST that you educate yourself about this, so that you can prevent someone from "injecting" malicious codes in your codebase.
You can check the following threads to have a better understanding of this security implication:
- Security -- how are malicious or insecure Packagist packages reported
- Packages using `replace` are in need
- Limit Replace / Provides to packages required by name in root package or any dep
- COMPOSER: REPLACE, CONFLICT & FORKS EXPLAINED