The ordered-list numbering used intval() on the "start" attribute inside a truthiness check, so a valid start="0" was treated as absent and the list restarted numbering at 1. Detect an explicit numeric start attribute with is_numeric() instead, so <ol start=\"0\"> renders as 0. 1. ...
2.3 KiB
fix: honor start="0" on ordered lists
Problem
Converting an ordered list whose start attribute is 0 produces the wrong
numbering: the list restarts at 1 instead of 0.
$converter = new League\HTMLToMarkdown\HtmlConverter();
$converter->convert('<ol start="0"><li>Item A</li><li>Item B</li></ol>');
// actual: "1. Item A\n2. Item B"
// expected: "0. Item A\n1. Item B"
<ol start="0"> is valid HTML, and CommonMark supports ordered lists that begin
at 0, so the resulting Markdown is incorrect. Any other start value (for
example start="120") works.
Root cause
src/Converter/ListItemConverter.php:55. The start value was read inside a
truthiness check:
if ($listType === 'ol' && ($parent = $element->getParent()) && ($start = \intval($parent->getAttribute('start')))) {
$number = $start + $element->getSiblingPosition() - 1;
} else {
$number = $element->getSiblingPosition();
}
\intval('0') is 0, which is falsy, so an explicit start="0" takes the
else branch and the list is renumbered from the sibling position (1-based).
The check cannot distinguish "start attribute absent" from "start attribute set
to 0", because getAttribute('start') returns '' when the attribute is missing
and intval('') is also 0.
The fix
Detect an explicitly provided numeric start attribute with \is_numeric()
instead of relying on the truthiness of intval():
if ($listType === 'ol' && ($parent = $element->getParent()) && \is_numeric($start = $parent->getAttribute('start'))) {
$number = (int) $start + $element->getSiblingPosition() - 1;
} else {
$number = $element->getSiblingPosition();
}
\is_numeric('') is false (missing attribute -> normal numbering) while
\is_numeric('0') is true, so start="0" is now respected. Existing values
such as start="120" are unaffected.
Test
Added an assertion in HtmlConverterTest::testLists:
$this->assertHtmlGivesMarkdown('<ol start="0"><li>Item A</li><li>Item B</li></ol>', "0. Item A\n1. Item B");
Gate
composer install && vendor/bin/phpunit --no-coverage
Result: exit code 0 - OK (50 tests, 230 assertions).
Counterfactual: reverting ListItemConverter to the intval() truthiness check
fails the new assertion - <ol start="0"> yields 1. Item A\n2. Item B instead
of 0. Item A\n1. Item B.