While working with legacy code upgrades, we often need to fix a line or two in 3rd party package in /vendor
.
You can fork it, but by that, you take manual responsibility for all the package updates. You can copy a package locally, which is faster, but disables package updates.
Or... you can use "composer patches"?
I'm currently working on one Nette project as a cleaning lady. After making code nice & shiny clean, we'll move from Nette 2.4 to Nette 3.0. During the cleaning process, we got into one delicate issue I'd love to share with you.
We used inject properties:
<?php
abstract class AbstractSomePresenter
{
/**
* @inject
* @var SomeDependency
*/
public $someDependency;
}
But since PHP 7.4, we could drop the @var
annotation:
<?php
abstract class AbstractSomePresenter
{
/**
* @inject
*/
public SomeDependency $someDependency;
}
But... this doesn't work in Nette 2.4. Aww.
We tried to add this feature by replacing native InjectExtension
with our own. But native extension is statically hardcoded, so there was no way to replace it.
What else we can do? We looked at GitHub for a commit that added this feature somewhere between Nette 2.4 and 3.0 (with git blame GitHub feature and look at specific lines).
We were lucky. It was just 2 lines that added this feature!
/vendor
?We needed the same commit in our codebase with Nette 2.4.
How can we do that?
/vendor/nette/di/*
manually❌
These options are slow or will keep the code changed only on your local machine = your composer install
would suck a long time.
Is there some automated way with all the benefits and almost zero maintenance?
✅
Idea behind composer patches is great, but the user experience with making the patch not so much. It's classic pixel coding - you have to edit the patch file the one slash or dot char. If you do it wrong, or the whole process collides with "fatal error". That's why there is over 10 comments under Czech post by Tomas Pilar, asking about the pixel coding.
I don't want developers to be frustrated over pixel coding. I want developers to play and explore their abilities to their limits.
We made a Symplify package that adds UX layer that handles the tedious maintenance for you.
composer require cweagans/composer-patches symplify/vendor-patches --dev
/vendor
file you Want To Change with *.old
SuffixFor example, if you edit:
vendor/nette/di/src/DI/Extensions/InjectExtension.php
The copied file would be:
vendor/nette/di/src/DI/Extensions/InjectExtension.php.old
if (DI\Helpers::parseAnnotation($rp, 'inject') !== null) {
- if ($type = DI\Helpers::parseAnnotation($rp, 'var')) {
+ if ($type = \Amateri\Reflection\Helper\StaticReflectionHelper::getPropertyType($rp)) {
+ } elseif ($type = DI\Helpers::parseAnnotation($rp, 'var')) {
$type = Reflection::expandClassName($type, Reflection::getPropertyDeclaringClass($rp));
Only *.php
file is loaded, not the *.php.old
one. This way, you can be sure the new code is working before you generate patches.
generate
command to Create Patch File and Register itvendor/bin/vendor-patches generate
The Symplify tool will generate patch files for all files created this way in /patches
directory:
/patches/nette-di-di-extensions-injectextension.php.patch
The patch path is created from the original file path, so the patch name is always unique.
Also, the configuration for cweagans/composer-patches
is added your composer.json
:
{
"extra": {
"patches": {
"nette/di": [
"patches/nette_di_di_extensions_injectextension.patch"
]
}
}
}
That's it!
Now all you need to do is run composer:
composer install
And your patches are applied to your code!
Happy coding!
Do you learn from my contents or use open-souce packages like Rector every day?
Consider supporting it on GitHub Sponsors.
I'd really appreciate it!