Problem/Motivation
I was updating Drupal 11.2.10 site to the 11.3.1 on Debian system with PHP 8.3.28 using drush. Update was successful but when I wanted to update database it crashed with this error:
TypeError: preg_replace(): Argument #3 ($subject) must be of type array|string, float given in preg_replace() in core/modules/system/src/Install/Requirements/SystemRequirements.php(1093)
Indeed, there are all sorts of problems from relying on unquoted version strings that are parsed as a float. For example, if the version "patch level" is a 0, the YAML parser will drop it:
version: 1.0
gets parsed as just "1".
Steps to reproduce
- Install Drupal core (any version)
- Create a custom module.
- In the
custom-module.info.ymlfile, put an unquoted version that looks like a float (version: 1.1). - Try to visit
update.php
Proposed resolution
It turned out that I have custom module where I use version attribute in the info.yml file, like this:
version: 24.0103
Line 1093 in SystemRequirements.php is
$version = preg_replace('/^(' . \Drupal::CORE_COMPATIBILITY . '\-)/', '', $required_file->info['version'] ?? '');
But from my file version value is coming as float.
It can be fixed on my end by updating that line with
version: '24.0103'
but maybe I'm not the only one who will face this issue.
Therefore, using unquoted version strings is now deprecated. People must always wrap their version strings in single quotes to ensure we're not getting unpredictable results and that the YAML parser will always treat them as strings. In 11.4.x and all of D12, this will hit a @trigger_error(), and with #3576313: Throw an exception in InfoParserDynamic::parse() for non-string versions we'll convert the deprecation to throwing an exception starting in 13.0.0.
Remaining tasks
Decide if we want afrom @alexpott in Slack: "I would - seems risk free imo"11.3.x/10.6.xMR(s) with just the fix to cast floats to strings, without the new deprecation message.- Commit MR !14791 to
mainand cherry-pick to11.x - Commit MR !14987 to
11.3.xand cherry-pick to10.6.x - Publish the CR
User interface changes
Introduced terminology
API changes
Data model changes
Release notes snippet
Using anything other than a string for the version key in in .info.yml files is deprecated. The version should always be wrapped in single quotes to ensure that the YAML parser will not treat it as a float. See the change record, The 'version' attribute in .info.yml files must be a string, for more information.
| Comment | File | Size | Author |
|---|
Issue fork drupal-3565033
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
quietone commentedHi, in Drupal core changes are made on on 11.x (our main development branch) first, and are then back ported as needed according to the Core change policies. Thanks.
Comment #3
drupalfan2 commentedThis patch should fix the bug.
Comment #4
jopdebeeck commentedBumped on this issue when updating to 11.3
The patch works, but for some reason I could not apply it through composer.
Attached is a remade version that does apply.
Comment #5
nagy.balint commentedI just run into this issue.
Not sure, but maybe dev version of some modules could cause it too.
I got float(2) on the string(6) "Splide" module.
in the info file I have
# Add a fake version so local drush and Git checkouts do not fail dependencies.
version: 2.0
And maybe that breaks it?
Comment #7
berdirIt's not about dev, it's about using unquoted strings and then the default yaml parser interpretes anything with a single . as a float. Using '2.0' in the example from #5 would.
I did just run into this one as well, I think it makes sense to fix this and cast explicitly. Possibly the cast should be earlier, where we parse the info file. The fix must be a merge request though.
Comment #8
sagesolutions commentedAlso ran into this when upgrading to Drupal 11.3.3. Turns out my custom modules info.yml files had
version: 1.0instead of the proper quoted stringversion: '1.0'Updating the version to a string allowed me to run the
drush updbcommand successfully.Comment #11
gino.nassi commentedParche #4 funciona correctamente
Comment #12
berdirI rebased on 11.x but discussed with @alexpott, we both agree that this should be cast into a string in \Drupal\Core\Extension\InfoParserDynamic::parse, next to the existing version check. Then we can be certain that any version is always a string anywhere we use it.
Not doing that change myself because I think I can still RTBC it if someone does prepare that.
Comment #14
dwwComment #15
dwwProbably goes without saying, but I also think
InfoParserDynamic::parse()is the right place for this cast.Comment #16
dwwDrat. I'm adding some test coverage to this in
core/tests/Drupal/Tests/Core/Extension/InfoParserUnitTest.php. Casting at this point doesn't fully solve it. I put:in a test case. It's parsed as
1, so when cast to a string, it's still just'1', not the'1.0'we're expecting. I'll push the test changes in a moment, but wanted to flag it here, first.Comment #17
dwwThe test coverage showed that for consistency, we don't want a closing period on the new exception message. That's what https://git.drupalcode.org/project/drupal/-/merge_requests/14791/diffs?c... is about.
But here's the badness:
https://git.drupalcode.org/project/drupal/-/jobs/8688837/viewer#L7968
Comment #18
dwwPer Slack thread about this, @alexpott suggested that the best solution to #17 is to fully deprecate using anything other than a string for the version. Created a CR about it, pushed a commit to add the
@trigger_error()in this case and expand test coverage to verify the deprecation (and casting behavior), and opened a follow-up to convert this to an exception in D13: #3576313: Throw an exception in InfoParserDynamic::parse() for non-string versions.Not sure if the deprecation message should be more accurate and say "... is deprecated in drupal:11.3.4 ..." or if we can leave it as "deprecated in drupal:11.3.0".
Comment #19
godotislateA few minor/nit-ish comments on the MR, but one about using
expectUserDeprecationMessage()instead ofexpectDeprecation()per #3497124: Deprecate expectDeprecation(), use PHPUnit's expectUserDeprecationMessage*() instead.Comment #20
dwwThanks for the review, and the tip about #3497124: Deprecate expectDeprecation(), use PHPUnit's expectUserDeprecationMessage*() instead. Applied all suggestions, and rebased to latest
main.Comment #21
dwwAsked about the end of #18 in Slack, and this is only going to land for 11.4.x, not 11.3.x. Changed deprecation message and expectation.
Comment #22
godotislateMR comments addressed and tests pass. The CR looks good as well.
A couple things while reviewing the IS just now:
SystemRequirements? I think no, but not sureI'm OK for RTBC once the IS has been updated if the test isn't deemed necessary.
Comment #23
godotislateOops meant to go back to NW for IS update.
Comment #24
dwwComment #25
godotislatelgtm
Comment #26
dwwAdded a link to the CR to the release note snippet.
Comment #27
nicxvan commentedI updated credit.
I just have one question, is the deprecation just to handle the 1.0 -> 1 change from comment 15 and 16?
Or is there another case we're trying to cover?
Comment #28
dwwPer the summary, if you have a float literal in the
.info.ymlfile for a custom module, there are currently fatal errors inSystemRequirementsfrom expecting a string and getting a float. The fix here is to cast any non-string scalar to a string.The deprecation is to warn people that relying on this string cast is a bad idea (because of the 1.0 -> 1 change, and potentially other weirdness from YAML trying to guess/simplify your version string if it looks like a float or int). Instead of trying to do anything fancy to try to handle numeric literals during YAML parsing to preserve what you put in your
.info.ymlfile, we're warning people they better wrap their custom version strings in'marks to ensure the parser doesn't mess with them.Per the CR, none of this matters for contrib modules hosted on d.o, since the d.o packaging script has always inserted the version (as determined by the tag being packaged) as a string. Huzzah ~20 years ago me. 😂
Just added my last question to the summary as a remaining task:
Decide if we want a
11.3.x/10.6.xMR(s) with just the fix to cast floats to strings, without the new deprecation message.Comment #29
berdirI'd appreciate a MR for 11.3, one less patch to maintain, on the other side, if we're deprecating anyway, I need to change the affected modules anyway.
Comment #31
dwwRe: fix-only backport. From @alexpott in Slack:
https://git.drupalcode.org/project/drupal/-/merge_requests/14987 targets 11.3.x branch, but I checked locally and it will cleanly cherry-pick into 10.6.x.
Pipeline is fubar due to the #3576458: [regression] Subsystem and Topics maintainers require access to re-run, trigger, or view tests evilness. Pipeline is running in the parent (Drupal) project, and I don't have permissions there to trigger the downstream full test job. 😢 However, the core unit tests already passed, which is the only thing this should impact. Wouldn't hurt to have someone other than me review the MR diff, but it's basically identical to the orig without the
@trigger_error()and expecting the deprecation in the test.I did keep the new exception for non-scalar version, since this is definitely completely broken:
😂
Comment #32
letmerungitlabtests commented@dww as a workaround you can create a second account.
You need to create the account, then go to that account on you main account to verify, then accept drupal terms of service, then accept terms of service on gitlab from gitlab code.
Then you can rerun jobs. Not ideal, but it's a workaround for the moment, I just tested it here.
Edit: I confirmed with @hestenet on slack that it's acceptable to do this.
Comment #33
godotislateI ran the 11.3.x pipeline manually and it passed finally.
Comment #34
quietone commentedYAML does use the term 'attribute' for the values.
Comment #35
quietone commentedI've updated the change record, so that should be checked.
Comment #36
dwwThanks for the review! Applied the suggestions. I was feeling icky calling them "attributes" (since that means something specific in PHP), but wasn't sure what else to use. 😅 Glad "value" is legit YAML-speak.
The CR edits mostly looked good, thanks for those, too. I did a few more tiny edits:
https://www.drupal.org/node/3576311/revisions/view/14296793/14296798
Back to NR. Hopefully the pipeline works, since it's running in the parent project and no one but core committers has permission to re-run individual jobs in there...
Thanks again,
-Derek
Comment #37
nicxvan commentedRe 34 assuming you meant does not use attribute, the Cr looks great!
However the main MR isn't passing tests so leaving it needs work
Edit crosspost, needs work though for the above.
Comment #38
dwwPretty sure https://git.drupalcode.org/project/drupal/-/jobs/8952387 are random package_manager build test fails. They all passed in the previously green pipeline: https://git.drupalcode.org/project/drupal/-/pipelines/758467
Since I'm a "developer", this pipeline ran in the parent, so only core-committers can re-run that job. Doesn't seem worth the $$ for a whole new pipeline to hope the random passes, so back to NR.
Comment #39
dwwIndeed: #3508109: [random test failure] Package manager random build failures
Comment #40
dwwAlso, just double-checked the 11.3.x / 10.6.x fix-only MR. There are no deprecations to fix. The only user-facing text there is:
So that should be good to go as-is.
Thanks,
-Derek
Comment #41
dwwTo be extra clear, that exception is being added to give a more reasonable WSOD message if you do something truly weird in your
.info.ymlfile.Current 10.6.x local site where I broke a contrib module with this in its
.info.yml:Right now, visiting extend page (or much of anything), results in:
With the MR diff applied:
Meanwhile, on that same local 10.6.x site, if I change
date_filter.info.ymlto include this:and visit
update.php, I get WSOD with:But if I apply the backport MR diff, I get a working DB update process. 🎉
Needs manual testing😅Comment #42
nicxvan commentedThose updates look good, tests are passing again!
Comment #43
dwwThanks @nicxvan!
Minor formatting fixes to remaining tasks.
Comment #44
alexpottCommitted and pushed 1bbd5cf0cb7 to main and 1c1e7f6d59f to 11.x. Thanks!
Committed and pushed d7a1176d78a to 11.3.x and 41b66710ac6 to 10.6.x. Thanks!