Problem/Motivation
The contributed module ecosystem has had to craft ways to provide backward compatibility between minor and major versions of Drupal core.
- Minor versions: silence runtime deprecations triggered while supporting the current major version's minor versions with security coverage.
- Major versions: prevent calling deprecated code that was removed while bridging support with the latest minor version of the last major version (9.5 && 10.0.)
It often looks like this:
if (version_compare(\Drupal::VERSION, '9.4', '>=')) {
// Do the new way introduced in 9.4, and supported in 9.5 and 10.0.
}
else {
// @phpstan-ignore-next-line
// Call the deprecated code for 9.2 and 9.3.
}
This is painful to copy around. It can be simplified if Drupal provided a utility class method or function that allowed invoking the correct code based on the provided version. I envision the signature being the following:
string $version
: The value to pass toversion_compare(\Drupal::VERSION, $version, '>=')
callable $current
: A callable to be invoked ifversion_compare
istrue
.callable $deprecated
: A callable to be invoked ifversion_compare
isfalse
This would also return any values just like \Drupal\Core\Render\RendererInterface::executeInRenderContext()
.
PHPStan's deprecation rule package can now define custom deprecation scopes (see https://github.com/phpstan/phpstan-deprecation-rules/pull/99.) The phpstan-drupal package can then consider this as a deprecated scope, removing the need for @phpstan-ignore-next-line
throughout backward compatibility layer code.
Some of my relevant blog posts on this topic:
- https://mglaman.dev/blog/adding-backward-compatibility-rector-rules
- https://mglaman.dev/blog/drupal-module-semantic-versioning-drupal-core-s...
Proposed resolution
Create the method backwardsCompatibleCall
on the \Drupal\Component\Utility\DeprecationHelper
class.
Here is an example:
$password = \Drupal\Core\Utility\DeprecationHelper::backwardsCompatibleCall(\Drupal::VERSION, '9.1.0', function () {
return user_password();
}, function () {
return \Drupal::service('password_generator')->generate();
});
Ideally this would be added to a version of Drupal 10 early enough that the Drupal 11 readiness work can leverage it. That means modules choosing to do so would need to drop previous minor versions of Drupal 10. So if this landed in 10.2 and 10.4 would be our last minor before Drupal 11, modules could safely support ^10.2.0 || ^11
and use this.
On of the goals is make the api surface as small as possible since this would not be used in core, but only in projects outside core.
Working proof of concept
In rector there is a working proof of concept.
Remaining tasks
Decide if this is a good ideaBikeshed wordsCreate change record- Agree on implementation and get it merged.
API changes
Introduces a new backward compatibility API.
Release notes snippet
A new DeprecationHelper API has been added to enable contributed modules to support multiple versions of core more easily. Contributed modules may add a minimum dependency on Drupal 10.1.3 if they want to start using this API.
Comment | File | Size | Author |
---|
Issue fork drupal-3371619
Show commands
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
mglamanComment #3
KingdutchI was on the fence, mostly because I'm unsure of the performance impact of encouraging closures which may live on the hot path. But I realize that the overhead is probably negligible given all the function calls already happening for a typical request.
I'd be all in on this if:
1. We make proper use of PHPStan from the get-go to help people with types. My proposal is
2. We build a PHPStan-Drupal rule that can check our declared Drupal support and easily tell us when to replace the backwardsCompatible call with the
current
call :D (I think that's a much easier rule to maintain than checking every possible if-statement with a `version_compare` in it.Comment #4
mglaman+1 thanks for showing the full impementation with PHPStan analysis! Super cool. Especially since it merged the return types as well.
I don't know about that. Because upgrade_status will run and it'll always say "hey, you don't need this anymore" when using contributed modules. I highly doubt anyone writing their own code would use this layer.
Comment #5
bbralaWell, as discussed earlier, this is extremely helpfull to get into core for the cycle to Drupal 11. One thing we might need to investigate is performance though. Not sure how many BC paths there are in core, but although callable is kinda clean, the other options to call external functions is faster, but perhaps not enough.
Also, shouldn't we handle arguments here also? Wouldnt we need to to call those callables with arguments sometimes? Not sure how aanlysis would react on that would we could have something like
$args
and unpack (...$args
) that in thebackwardsCompatibleCall
.Comment #6
bbralaHmm, the arguments could live in the anonimous function couldn't they...
Comment #7
mglamanThe performance check is around the
if
statement.I figure folks would leverage
use ()
to pass in upper scope variables to their closures, like the do withexecuteInRenderContext
Comment #8
catchFor core there are no core-version-specific bc paths, so it can't use this pattern, but that also means there's no inherent performance implication to adding it, it'd just be about whether a contrib module is using it in the critical path.
Comment #9
bbralaValid point.
While looking at how this would work, and trying to think up tests, I did find a possible weirdness.
The version_compare function in PHP compares versions, but does resolve dev versions below released versions. Also, it cannot parse 10.2.x-dev for example.
This is mostly a problem for tests I'd recon, and if someone runs a dev version of core, things would break then? I don't really see another way to get the released version of core in the dev branch. Also, the current version is 11.x-dev.
$current()
?Comment #10
catchCan we normalize 10.2.x-dev to 10.2 maybe?
Comment #11
bbralaWe could, but rather not do that by default since that would be overhead. Also a check on the last few characters which is pretty fast though.
But also, what would the
\Drupal::VERSION
be when we work with amain
branch? Will this be the same? Will this be main? Will this be next major? Will this be next minor?Comment #13
catch@bbrala I think we need to do #3364646: Add a branch alias for 11.x for 11.x, once that is in, the 'version' will be 10.2.x-dev, and then if that works, it'll also work for 'main' - so we'd need to increment Drupal::VERSION every six months with whatever the next branch to branch off 'main' is going to be.
Comment #14
bbralaThat sounds good. I'll have a stab a that so we have something to shoot at.
Comment #15
mglamanWhat if:
* If value is `main` assume fallback (unparseable)
* Use explode to rebuild major/minor if -dev is not parseble.
This does add performance overhead though unless we statically cache version comparison values or the parsed version
Comment #16
bbrala@mglaman I'd probably not so the whole parsing dance, but check if ends with
.x-dev
(very cheap) and if so remove it. This is relatively cheap, and then we still use version_compare, which feels nicer to keep. I think @catch also means that main will not be a drupal version, but it will bemajor.minor.x-dev
when we start using main. Until then, this same logic would result in a version comparing to11
which should parse fine also.In other news
I've been thinking about this a little more, and was thinking about testing. If we want to test this (even though this is a pretty simple method) we'd need a way to mock the version. This is a bit hard when letting the code live in
\Drupal
. Secondly, when i look at the goal of that class: "Static Service Container wrapper." Which makes this method feel very out of place.I was then looking at where this might live if not in that class. Wouldn't an Utility class make more sense? I understand utility classes are a bit of a codesmell in some instances, but the fact it lives in
\Drupal
really feels wrong.Some possible alternatives:
\Drupal\Core\Helper\Deprecation::backwardsCompatibleCall
\Drupal\Core\Deprecation::backwardsCompatibleCall
\Drupal\Core\DeprecationHelper::backwardsCompatibleCall
This would also mean we can mock the constant for testing.
Comment #17
bbralaI've made an implementation in a way that feels right to me.
Drupal\Core\Utility
namespace for the helper.normalizedDrupalVersion
to the helper so testing is a little easier.Setting this issue to NR so we can move along with something concrete to talk about.
Comment #18
bbralaOne of the questions i might have; should .x be rewritten to
.0
or to.99
? Or is that sematics. Deprecations to not appear in patch releases right? So something is always introduced inx.x.0
, therefor the whole minor version should be.0
always when on a dev version?Comment #19
bbralaSince #3364646: Add a branch alias for 11.x has been merged, the 'main' discussion is deprecated i think :)
Comment #20
KingdutchI think the implementation looks good! On Slack we discussed delegating the version normalization to Composer. However the needs of Composer are slightly different to our own in how we want to treat `-dev`. We also expect this code to be usable in a hotpath, which Composer's more resilient semantic versioning isn't. I'm in favour of using Björn's code that is simple, performant and covers our use case :D
The code in the MR looks good and I think the test coverage is sufficient. I do wonder why we chose extending the class we're testing. Is it not better to mock just the method we want to change? That way we can also set an expectation on the protected method being called, that allows us to change the internals of the class and provides a more helpful test breakage if we no longer call the method the test relies on. (In the current scenario we could stop using the protected method and it might be unclear why our tests fail).
I was thinking about whether we could annotate any PHPStan types to make it clearer that the version string should be a version. PHPStan allows us to limit it to specifically
\Drupal::VERSION
but since it's a changeable argument that's probably undesirable (and is not satisfiable in the test). PHPStan currently doesn't have string-templates for types (that's a feature we should steal from TypeScript) but it does havenon-empty-string
here.I don't know what coding standards currently allow, whether we could do
@param non-empty-string $version
or whether we can add a separate@phpstan-param non-empty-string $version
at the bottom to help PHPStan users :DComment #21
bbralaThis is true, I ended up like this because i couldn't make it private and mock it, that would requite visibility changes, thus it needed to be protected. Then I thought, this way I minimize mocking needed. But your argument that we can mock that method then set expectations is a good one, since that would indeed ensure we are not secretly removing the dependency.
Hmm, if we mock it, we don't need the argument. But then the normalization is pretty untestable since we cannot influence the version used. We cannot mock a class constant and we cannot get a Drupal version from a method (although there is discussion about something similar in the linked issue you provided). The fact the API is smaller if it does not allow for an argument is something i would like though.
Comment #22
bbralaRemoved normalizedDrupalVersion method in favor of a protected var with Drupal::VERSION as default value. Since mocking static methods is not possible this is a way to test without introducing an extra api surface with the normalizedDrupalVersion static method.
So, the api surface is minimized a little more, inlines the normalization in the method which removed the need for the extra class. Unfortunately mocking cannot mock static methods all to well, so this seems like the most clean way to test. The testing double now doesn't overwrite any code that is used to perform the action, only adds a setter to the class.
Comment #23
bbrala@catch i do understand there is some overlap in the related issues, but i would prefer to not wait until that is in. We could always just change this when those land.
On order to make sure we notice i added a new test that checks current core version against a change introduced in 9.5.0 and 99.99.99. If \Drupal::VERSION wil be removed, this will trigger a failure since that constant would then be removed.
Comment #24
mglamanThe code looks good to me. I just wish we could make
DeprecationHelper
final. But then that breaks the testability.One fix/approach is to add a
$currentVersion
tobackwardsCompatibleCall
. Now, that seems a bit silly on the surface. But right nowDeprecationHelper::backwardsCompatibleCall
only works with deprecated Drupal core code. What about deprecated contributed code? By expanding the signature we add more boilerplate but make it more flexible! And testable!Comment #25
mglamanI'm confident in the tests passing and I'm going to go ahead and RTBC this issue. Drupal core now provides a great utility that contributed modules can use to balance deprecations from Drupal core or their own contributed module dependencies.
Comment #26
bbralaWrote a CR. :)
Comment #28
dwwThanks so much for this! 💗🙏 I love to try to support as many versions of core as possible, and this will make it much easier. Tagging for DX.
The CR mostly looked great, thanks! I made a few slight edits to the title and content.
I spotted a few trivial "fixes". Easier to just push commits to fix them than open new MR threads. 😅 Leaving RTBC since they're so trivial, but if anyone wants to re-review, please do.
Thanks again!
-Derek
Comment #29
bbralaChanges are indeed minimal, thanks for those :) No need to go back to NR
Comment #30
mondrakeVery nice, but I think at least one of my added comments NW.
Comment #31
bbralaComment #32
bbralaAdded credits for mglaman for posting and reviewing and slacktalks
Kingdutch for review.
Dww for small optimizations and review
Mondrake for review
Catch for related issues and partipation
Comment #33
mondrake1.
declare(strict_types=1);
can be added to the test file, too.2. What is the purpose of the $version argument, besides testing? I suppose normally runtime code calls would have
\Drupal::VERSION
there. So wouldn’t it make sense to have a wrapper method that does not have $version as an argument, retrieves \Drupal::VERSION, and passes it tobackwardsCompatibleCall()
?3. I was thinking if we also add a $removedVersion argument, we could check if the current version is greater or equal than that, and throw a deprecation in that case. So when a new branch is opened, we immediately find where code should be adjusted to make the call to new method straight. It would require adding an ignored deprecation in the relevant file, but then in followup issues the job would be done.
Comment #34
mondrakeIs there at least one example in core that can be converted as part of this issue?
Comment #35
bbrala1. True. Simple minimal fix.
2. Also; isolates the class from core more, also enables projects to check for their own version if they want. But most impantly is the fact the class is less prone to extention and the api surface is 1 single method.
3. I think this should stay as is, it's a helper to allow projects to support current major plus next, core only really has 2 supported versions always.
In regards to your next comment, no this won't be used in core probably since there never really a compatibility layer as @catch also mentioned earlier in the issue.
Comment #36
Ghost of Drupal PastFirst, maybe
$normalizedVersion = str_ends_with($version, '-dev') ? str_replace(['.x-dev', '-dev'], '.0', $version) : $version;
should bepreg_replace('/(\.x)?-dev$/', '', $version)
instead?Now I typed the following up to suggest to be included in the doxygen, recognizing the advantage of the function call being an expression vs
if
which is not. But we are on PHP 8 which hasmatch
so my doxygen suggestion would beBut this lead me to think, is this really any simpler? Couldn't we simplify and make the match more expressive instead?
Where
\Drupal::CURRENT
and\Drupal::DEPRECATED
are justTRUE
/FALSE
andneedsDeprecatedCallBefore
is an appropriate wrapper ofversion_compare
with theVERSION
constant. And we don't need // @phpstan-ignore-next-line because -- I hope this is as easy as I present it here --\Drupal::DEPRECATED
tells a deprecated call follows. We could put this pattern in the doxygen ofneedsDeprecatedCallBefore
and every change notice where something gets deprecated. Hopefully it is also possible to find the parent expression and automatically replace the wholematch
expression with the value of\Drupal::CURRENT
when one wants to clean this up in the next major (edit: from a brief check of nikic/PHP-parser, it seems to me this would require looking at the arms of a match and if they are \Drupal::CURRENT and \Drupal::DEPRECATED then replace the entire match -- but it seems to be possible indeed).What do you think?
Comment #37
mglamanMy concern: using match and adding two new constants seems like a lot of overhead versus a ternary operator.
I'll be updating phpstan-drupal to consider any code within
backwardsCompatibleCall
to be a deprecated scope.I don't know if I could do the same by verifying the code was called within a specific match.
Comment #38
bbralaAlthough you probably could using maych, the pattern I proposed will be easier, also when working with rector I think. This change is to facilitate backwards compatibility calls for projects, core won't use them. This also enables easier bc patches for project update bot through rector.
I'd rather change as little as possible in core and couple as loosely to core as possible, since this is not to be used in core, just supplied to help the ecosystem. Therefor things like new constants are something I'd rather avoid.
Regarding the preg_replace, I used the sting functions because of the performance optimization, and my rule of thumb, if I don't need a regex I won't use one. I also mentioned this in the mr comments I think.
So thanks for the suggestions, but I feel the current setup is better for the goals we have with this code.
Comment #39
bbralaA working (in unit test) proof of concept of this in Drupal Rector.
Comment #40
bbralaUpdated IS
Comment #41
mondrakeIf this is meant to be independent from core, then I think the class should live under Component\Utility, not under Core\Utility.
Comment #42
bbralaYou are completely right @mondrake. Quoting from the readme of the Component namespace.
Which we realized by adding the extra version parameter.
Comment #43
bbralaThink everything has been adressed :)
Comment #44
KingdutchThe request by mondrake to move this to the right namespace has been resolved. My questions about the comments have also been resolved which makes it clearer for people that this is not Drupal core specific. The changes to the class also improved testability and the test coverage is good. With all that together I think this is ready to be merged!
I've slightly changed the related change record to make it clearer what the change from
version_compare
looks like. The previous version of the change record used different version numbers as arguments which might be confusing when trying to use the CR to update your own code.Comment #45
andypostI think the test should use fast-call syntax https://php.watch/versions/8.1/first-class-callable-syntax to negate effect of condition
Or at least
fn() =>
as #36 suggestedAdditionally 8.2 changes https://wiki.php.net/rfc/deprecate_partially_supported_callables IMO is the reason to have example inline with current best-practices
Also named arguments are helpful here as
\Drupal::VERSION
must be passed which I find as bad DX (maybe better to change the value via reflection)Here's interdiff and patch of proposed changes, I did not add to MR to get opinions
Comment #46
andypostFixed patch, interdiff against MR
argument names confusing a bit
Comment #47
bbralaOk, i agree on
fn() =>
but the fast callable might complicate the scope checking for PHPStan. I'll change to the arrow functions.Hmm, I understand where you are coming from, but this opens up usage for other projects. Also I do like the fact it relies on nothing in Core this way. So I understand it's a little weird perhaps, but it has it's pros. Rather not change that.
This was the best I could come up with. Al the options i could think off didnt make more sense. Any suggestions?
Comment #48
bbralaSome options for the vars:
Naming is hard.
Comment #49
heddnbackwardsCompatibleCall(string $currentVersion, callable $currentCallable, string $deprecatedVersion, callable $deprecatedCallable)
Agreed that naming is hard. What about this? Slightly changed order for parameters.
Comment #50
catchReading through the MR now, a lot happened here since my last comment, but #49 seems quite good to me?
Comment #51
andypostInterdiff against MR, implements #49
Also there's measurments https://gist.github.com/donquixote/85efcca90056111e967dd14cb1f9de9c from #2982949-113: Introduce CallableResolver to help standardise the DX and error handling for callbacks across various subsystems
Comment #52
andypostStill not clear how to pass arguments for both callables
As component mention of Drupal should be removed
Comment #53
andypostLooks #36 is the most optimal way to pass arguments
Comment #54
bbrala#49:
I really like that. It connects the arguments to eachother. It might still feel weird in some cases, but it seems to tell the story as good as it gets. So i updated the code to use that style.
Also removed Drupal from the param comments. And fixed the comment by @catch
@andypost I don't feel the callable syntax you propose in your patch is something i want to suggest to developers in this case. These are a few unknowns in that regards. This code should mostly be used for deprecated calls to core functions and such, and mostly will come from automated fixes by rector i recon. This means we will probably just keep them as tightly scoped in the static call as possible partly because in rector this is way easier, partly because this makes resolving the scope of the code path way easier. But people are free to use any syntax they like ;)
I hope we can now agree on this implementation and pass this to the committers :)
Comment #55
KingdutchThat's a lot of activity since I RTBC'd, I think the previous version was better in most regards though.
1. I agree with Björn that the indirection in the test is not a good idea.
fn () => "current"
achieves exactly the same result. It looks like we add a lot of noise just to make use of a new PHP feature?I can see that we're preparing for some deprecations and using a new PHP 8.1 feature, but the test wasn't actually suffering from any of the problems that those things address, so we're just adding complexity.
I see the performance comment in #51 but we're changing a simple arrow function created in a loop for an arrow function created in a loop with another indirection through a function call.
2. I think moving the version parameters around is a step back in readability for non-simple uses. The code that this is to replace is very simple in structure:
if {version1} {version2} {then} {else}
which maps nicely to the original function definition but no longer works now. An example of a non-simple use case:This now requires scrolling to compare the versions for the code rather than being able to see it at the start, that also makes automatic grepping/searching of two version strings together more difficult (e.g. I can't do
["'](\d+\.?)+["'],\w*["'](\d+\.?)+["']
anymore).3. I don't think this is the right place to introduce named arguments since all arguments are required. Especially with modern IDEs I feel like this solves for a problem that doesn't exist. Named arguments are specifically useful if we want to skip defaults but there's no need for that here. All arguments are required arguments.
4. We renamed the arguments to be focused specifically on deprecation, which I can understand from the IS. However the original naming "introduced in" in my opinion makes it clearer that the hidden check is
>=
and also makes the function useful for library improvements such as(Yes you should probably use function_exists here, but I can imagine there are similar examples where you might not want to do that because you're loading some service in a library that didn't exist before)
Comment #56
bbralaedit
I'm actually coming to the conclusion though that we can have both world in a way with your suggestion. I like the 'mental' coupling of var and callable, but that could also be achieved with the naming and keep tell the following story:
And there is personal flexibility in argument order with named arguments.
I'm a bit at a loss here to to get consensus though.
/edit
I'm gonna keep the MR updated on my current view of the best idea.
Comment #57
KingdutchRe 2/3. My compromise here would be to go back to the older version of (, , ,
). Especially if we want to encourage named paramters, that's precisely the point where order no longer matters. So this makes it similar to the old
if (version_compare(<version>, <version>) { <new> } else { <old> }
if no named parameters are used, and the test can stay as is with named parameters in a different preferred order.4. I agree it might not get usage outside of Drupal, but I would hope (especially with our move away from hardcoding
\Drupal::version
) that it can get used for broader usage than just deprecation. I don't even feel that's necessarily looking at a broader view, just another use-case. For example maybe an optional new parameter that's being added in Symfony 5.2Comment #58
bbralaComment got eaten by d.o... recommenting
Comment #59
andypostRe #57.4 - exactly because arguments can be different for old and new versions
about naming - we using "deprecated in" all over codebase (SF needs to check)
and there was an idea to create
deprecated.module
in core few timesComment #60
andypostThe reason for 8.1 is because it affects performance and 8.0 security support expire in 3 month https://www.php.net/supported-versions.php
To run new code on old PHP versions there's
symfony/polyfill-php8*
packages (in core)Comment #61
bbrala@Kingdutch I'm actually coming to the conclusion though that we can have both world in a way with your suggestion. I like the 'mental' coupling of var and callable, but that could also be achieved with the naming and keep tell the following story:
And there is personal flexibility in argument order with named arguments.
I'm a bit at a loss here to to get consensus though.
Comment #62
mglamanI don't exactly know why we're debating the kind of callable in the test. It's a test here. Keep it at arrow functions. That's nit picking the approach over minor performance. It's easier to read as-is with arrow functions.
I do not like this:
It should be
Group the parameters so it's easier to read as @Kingdutch has said.
We're not. We can remove the usage. I used it in the issue summary to help document what I expected it to look like via pseudo code. If named arguments are holding us up, can we please just remove their usage in the test.
Comment #63
bbralaOops, will fix later
Comment #64
bbralaFirst off, thanks everyone for the feedback and suggestions, didn't think this would actually end up a pretty active issue :)
In regards of the DeprecationHelper class I think its current form is as good as it gets. I lean towards the current argument setup and think this is good. Especially considering the scope of the change being basically outside of core.
People are free to callback as they wish, but the example will be with the current kind of function design. We will use this helper in update bot to probably also do some missing 9.x => 10 deprecations (which also need 7.4 support for now), so for now i want to keep it simple. We are still investigating how the different callback designs will affect PHPStan, this will probably be leading in how the callbacks will be applied by Rector. If all is well, when we start building 10 -> 11 pachtes we will consider all options for the callbacks, and try and be as optimal (and readable) as possible.
Hopefully we can angree on the current setup for the main class being good ^^
Credit added: @andypost for review and discussion, @heddn for good suggestion and slack, @Charlie ChX Negyesi for an indepth comment on the issue.
Comment #65
KingdutchWasn't set to "Needs review" in #63 but the test is green so assuming this was the goal.
The version that's now in the MR looks good to me and I'd be happy to see it merged.
Comment #66
dwwWow, tons of action in here since I last looked. 😅
Mostly still very happy with the state of the MR. My only lingering concerns come from this now being a
Component
, and intended to be used outside of Drupal(?):-dev
normalization to have semi-Drupal-specific logic?Component
, maybe that language should be even more general with something like "a call site" or "calling code" or something.Maybe this should be remain core utility, and not a component, after all. But I don't feel that strongly on either point, and am happy for committers to decide.
Thanks, everyone!
Comment #67
andypostComment #69
catchI think this is OK as a component - while the -dev handling does the opposite of what composer does, I don't think it could happen differently here, and the fact composer has -dev handling means that the concept in general is not a Drupalism. Whether anyone ever uses our components outside core is hard to find out, but if we don't put stuff there they definitely can't.
Committed/pushed to 11.x (10.2.x), thanks!
An issue here is that contrib won't be able to adopt this until support for 10.1 is dropped which is in approximately 9 months. Given this is 100% self-contained, do we want to consider an exceptional exception and backporting it to 10.1? That would mean that contrib modules using it would need to declare a dependency on > 10.1.4 or whichever release it goes out in, so I don't know if that would really help or not. But leaving open for discussion.
If we really made use of the components, contrib could require the 10.2.x version of the component by itself, but... let's not get into that.
Comment #70
dww+1 to backports. Core itself doesn’t call this, so it can’t possibly be a disruptive change to include it. It can only help the contrib ecosystem, not harm it, even if you need to require at least 10.1.4 or whatever to use it.
Thanks!
-Derek
Comment #71
bbralaYeah if possible to backport to earlier version that would be great, and would make the timing for starting to use this in rector a lot easier since this means we can start using it asap to create rules for Drupal 10 -> 11.
The fact is that this is a completly isolated class from core, so there is no risk to degrade.
So as i asked earlier, a backport would be something the community would most probably benefit from with almost no downside to the stability of core.
Comment #72
longwave+1 to backporting as it's self contained and can help contrib.
Also published the changed record, on 10.2.0 for now but will need updating if we backport.
Comment #74
catch@xjm also +1d this in slack, but said we should add it to the release notes for the next 10.1 patch release. Adding tags and marking needs work for that bit.
Comment #75
bbralaThank you! 😊
Comment #76
longwaveAdded a short release note, linking to the change record.
Comment #77
bbralaReleasenote looks great. Setting RTBC
Comment #78
xjmAnything that is blocking or soft-blocking for the major version compatibility tooling can be major, IMO, and a task.
Comment #79
xjmCherry-picked to 10.1.x. Thanks!
Comment #81
mglaman💙 thanks everyone; it's great to see this. I really believe it'll ease pain for maintainers in the ecosystem and help us automate fixes.
Comment #83
claudiu.cristeaMaybe a dumb question: Why not back-porting this helper in all Drupal 9 versions (even not supported)? What exactly would be the risk? I really need something like this NOW, when I port contribs from 9 to 10
Comment #84
Gábor HojtsyAdding to release highlights for 10.2.0 to make a bigger splash about this :)