Skip to content

[BUGFIX] Read project version from the DOM so "0.10" is not coerced to 0.1#1345

Open
CybotTM wants to merge 1 commit into
phpDocumentor:mainfrom
netresearch:fix/xml-version-string-coercion
Open

[BUGFIX] Read project version from the DOM so "0.10" is not coerced to 0.1#1345
CybotTM wants to merge 1 commit into
phpDocumentor:mainfrom
netresearch:fix/xml-version-string-coercion

Conversation

@CybotTM

@CybotTM CybotTM commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Problem

A guides.xml with <project version="0.10"> is rendered with the version 0.1 (title, objects.inv, every |version| substitution). Any round / trailing-zero two-part version is mangled:

guides.xml parsed as
0.10 0.1
1.0 1
1.10 1.1
13.0 13
0.9, 13.4, 1.2.3, main unchanged

It surfaced on docs.typo3.org for netresearch/nr-vault and netresearch/nr-llm, whose 0.10 / 0.12 releases were published under 0.1.

Cause

XmlFileLoader parses guides.xml with XmlUtils::convertDomElementToArray(), which runs phpize() on every attribute value. phpize("0.10") returns the float 0.1 (it is is_numeric), which then stringifies to "0.1".

There was already a workaround — an escaped version syntax (version="'3.0'", single-quoted to dodge phpize) plus a beforeNormalization that stripped the quotes again. But that runs after phpize has already discarded the digit (0.100.1), so it can never recover it.

Fix

Read the <project> attributes (all strings) directly from the DOM, and detach the element before convertDomElementToArray() so phpize never touches them. The version is read correctly at the source, instead of being coerced and patched up afterwards. The <guides> attributes that genuinely want coercion (links-are-relative, max-menu-depth, …) are unaffected.

Because the root cause is fixed, the beforeNormalization workaround in the Symfony config is removed — escaping is no longer needed to write a correct version.

Backward compatibility

Projects that adopted the escaped version workaround for this very bug may still have <project version="'3.0'"> in their guides.xml. To keep those rendering 3.0 (and not the literal '3.0'), the surrounding single quotes are still stripped for version and release — the same trim($value, "'") the removed beforeNormalization did, now applied in XmlFileLoader when the attributes are read. New files don't need it: write the version directly, version="0.10" / version="3.0".

Before / after

<project version="0.10"> as loaded by XmlFileLoader:

Before

before

After

after

Tests

Two integration fixtures under tests/Integration/tests/meta/:

  • version-from-guides-xml — unescaped version="0.10" (with release="3.0.0" as a non-coerced control); asserts the rendered |version| stays 0.10. Passes with the fix and fails without it (without it: version 0.1).
  • version-from-guides-xml-quoted — the legacy quoted form version="'3.0'" / release="'3.0.0'"; asserts it still renders 3.0 / 3.0.0, covering the backward-compatibility path above.

Integration suite passes locally (228 tests). CI is green across PHP 8.1–8.3 (highest / locked / lowest): unit, integration, functional, Coding Standards, PHPStan and architecture checks.

@CybotTM
CybotTM force-pushed the fix/xml-version-string-coercion branch 2 times, most recently from ffab307 to fcc475c Compare June 16, 2026 22:01
@CybotTM
CybotTM marked this pull request as draft June 17, 2026 05:38
@CybotTM
CybotTM force-pushed the fix/xml-version-string-coercion branch from fcc475c to 7d907c6 Compare June 17, 2026 05:51
@CybotTM CybotTM changed the title Keep version strings like "0.10" intact when loading guides.xml [BUGFIX] Read project version from the DOM so "0.10" is not coerced to 0.1 Jun 17, 2026
@CybotTM
CybotTM force-pushed the fix/xml-version-string-coercion branch 2 times, most recently from 6b70577 to 6503239 Compare June 17, 2026 06:19
@CybotTM
CybotTM marked this pull request as ready for review June 17, 2026 06:28
@CybotTM
CybotTM force-pushed the fix/xml-version-string-coercion branch from 6503239 to c921c2c Compare June 18, 2026 20:11
@garvinhicking

Copy link
Copy Markdown
Contributor

Thanks - I can't really judge the impact of this and hope @jaapio can give some feedback. I remember having stabbed at this and not being able to resolve this.

@CybotTM
CybotTM force-pushed the fix/xml-version-string-coercion branch 2 times, most recently from f346063 to c748708 Compare June 24, 2026 15:26
…o 0.1

A <project version="0.10"> in guides.xml was rendered as version 0.1
(title, objects.inv, every |version| substitution). XmlFileLoader parses
guides.xml with XmlUtils::convertDomElementToArray(), which runs phpize() on
every attribute value, coercing version-like strings into numbers: "0.10"
becomes the float 0.1, "1.0" becomes 1, "13.0" becomes 13.

Read the <project> attributes (all strings) straight from the DOM, and detach
the element before the conversion so phpize never sees them. The version is now
read correctly at the source instead of being coerced and patched up afterwards,
so the beforeNormalization workaround in the Symfony config is removed.

Writing the version directly (version="0.10") now just works. The previous
"escaped version" workaround -- version="'3.0'" with single quotes to dodge
phpize -- is no longer necessary, but existing guides.xml files may still use
it, so the surrounding single quotes are still stripped (for version and
release) to keep those files rendering 3.0 rather than the literal '3.0'.
A regression fixture covers the quoted form.

Reported on docs.typo3.org for netresearch/nr-vault and nr-llm (0.10 / 0.12).

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
@CybotTM
CybotTM force-pushed the fix/xml-version-string-coercion branch from c748708 to 555a67e Compare July 1, 2026 12:01
linawolf pushed a commit to TYPO3-Documentation/render-guides that referenced this pull request Jul 14, 2026
## Problem

For a package with both `0.1` and `0.10` (e.g.
[netresearch/nr-vault](https://docs.typo3.org/p/netresearch/nr-vault/0.10/en-us/))
the version switcher is wrong in two ways:

- **`0.10` is sorted last** instead of first — it should appear right
after `main`.
- **Opening the `0.10` page pre-selects `0.1`** in the dropdown.

## Cause

`versions.js` sorted by `parseFloat(v)`, and `parseFloat("0.10") ===
0.1`, so `0.10` ties with the `0.x` group and lands at the bottom.
Pre-selection compared against the rendered `data-current-version`
attribute, which is `"0.1"` on the `0.10` page because the version
string `"0.10"` is numerically coerced to `"0.1"` server-side (a
separate, deeper bug in the render pipeline).

## Fix

- Sort each dotted version component **numerically** (`main` first, then
highest version), so `0.10` > `0.9` > … > `0.1`.
- Derive the active version from the **page URL** (the authoritative
source, e.g. `…/0.10/en-us/…`) instead of the coercible
`data-current-version` attribute. This makes pre-selection correct
regardless of that server-side coercion.

`resources/public/js/theme.min.js` is rebuilt (grunt uglify).

## Before / after

The version dropdown on the `nr-vault` `0.10` page:

**Before**


![before](https://raw.githubusercontent.com/CybotTM/render-guides/version-sort-screenshots/before.png)

**After**


![after](https://raw.githubusercontent.com/CybotTM/render-guides/version-sort-screenshots/after.png)

## Tests

New `tests/js/versions.test.js` (vitest/jsdom) asserts both the sort
order (`main, 0.10, 0.9, … 0.1`) and that the `0.10` page pre-selects
`0.10`. Both **pass with the fix and fail without it**.

## Related

The wrong pre-selection has a second, deeper cause that this PR does
**not** rely on: the `0.10` page is served with
`data-current-version="0.1"` (and title / meta `0.1`) because
`guides.xml`'s `version="0.10"` is numerically coerced to the float
`0.1` while parsing (Symfony `XmlUtils` phpize). That is tracked in
#1293 and fixed upstream in phpDocumentor/guides#1345. This PR makes the
switcher correct regardless — it derives the active version from the
page URL — so the two fixes are independent.

---------

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
typo3-documentation-team pushed a commit to TYPO3-Documentation/t3docs-typo3-docs-theme that referenced this pull request Jul 14, 2026
## Problem

For a package with both `0.1` and `0.10` (e.g.
[netresearch/nr-vault](https://docs.typo3.org/p/netresearch/nr-vault/0.10/en-us/))
the version switcher is wrong in two ways:

- **`0.10` is sorted last** instead of first — it should appear right
after `main`.
- **Opening the `0.10` page pre-selects `0.1`** in the dropdown.

## Cause

`versions.js` sorted by `parseFloat(v)`, and `parseFloat("0.10") ===
0.1`, so `0.10` ties with the `0.x` group and lands at the bottom.
Pre-selection compared against the rendered `data-current-version`
attribute, which is `"0.1"` on the `0.10` page because the version
string `"0.10"` is numerically coerced to `"0.1"` server-side (a
separate, deeper bug in the render pipeline).

## Fix

- Sort each dotted version component **numerically** (`main` first, then
highest version), so `0.10` > `0.9` > … > `0.1`.
- Derive the active version from the **page URL** (the authoritative
source, e.g. `…/0.10/en-us/…`) instead of the coercible
`data-current-version` attribute. This makes pre-selection correct
regardless of that server-side coercion.

`resources/public/js/theme.min.js` is rebuilt (grunt uglify).

## Before / after

The version dropdown on the `nr-vault` `0.10` page:

**Before**


![before](https://raw.githubusercontent.com/CybotTM/render-guides/version-sort-screenshots/before.png)

**After**


![after](https://raw.githubusercontent.com/CybotTM/render-guides/version-sort-screenshots/after.png)

## Tests

New `tests/js/versions.test.js` (vitest/jsdom) asserts both the sort
order (`main, 0.10, 0.9, … 0.1`) and that the `0.10` page pre-selects
`0.10`. Both **pass with the fix and fail without it**.

## Related

The wrong pre-selection has a second, deeper cause that this PR does
**not** rely on: the `0.10` page is served with
`data-current-version="0.1"` (and title / meta `0.1`) because
`guides.xml`'s `version="0.10"` is numerically coerced to the float
`0.1` while parsing (Symfony `XmlUtils` phpize). That is tracked in
#1293 and fixed upstream in phpDocumentor/guides#1345. This PR makes the
switcher correct regardless — it derives the active version from the
page URL — so the two fixes are independent.

---------

Signed-off-by: Sebastian Mendel <info@sebastianmendel.de>
Signed-off-by: Sebastian Mendel <github@sebastianmendel.de>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants