Fix negative SimpleXML offsets aliasing the first element - #23068
Open
iliaal wants to merge 1 commit into
Open
Conversation
sxe_get_element_by_offset scanned with nodendx <= offset, so a negative offset skipped the loop and returned the node it started from. Reads and isset() reported the first element, and a write overwrote it. Negative offsets now miss, and writing to one warns like an out-of-range positive offset instead of creating a node. Closes phpGH-23068
iliaal
force-pushed
the
fix/sxe-negative-offset
branch
from
August 5, 2026 13:28
b8b2e9b to
7bdf744
Compare
Member
|
is a bug indeed, however can the following test being added ? --TEST--
Integer offsets that cannot resolve must never alias or mutate a node
--EXTENSIONS--
simplexml
--FILE--
<?php
function fresh(): SimpleXMLElement {
return simplexml_load_string('<r a="1" b="2"><item>a</item><item>b</item><item>c</item></r>');
}
function state(SimpleXMLElement $x): string {
return trim(strstr($x->asXML(), '<r'));
}
echo '== element list ==', PHP_EOL;
$x = fresh();
var_dump(isset($x->item[-1]));
var_dump($x->item[-1]);
$x->item[-1] = 'Z';
echo state($x), PHP_EOL;
unset($x->item[-1]);
echo state($x), PHP_EOL;
echo '== single element (SXE_ITER_NONE) ==', PHP_EOL;
$x = fresh();
$n = $x->item[0];
var_dump(isset($n[-1]));
var_dump($n[-1]);
$n[-1] = 'Z';
echo state($x), PHP_EOL;
$n[5] = 'Y';
echo state($x), PHP_EOL;
unset($n[-1]);
echo state($x), PHP_EOL;
echo '== nested write ==', PHP_EOL;
$x = fresh();
try {
$x->item[-1]->kid = 'K';
} catch (Throwable $e) {
echo get_class($e), ': ', $e->getMessage(), PHP_EOL;
}
echo state($x), PHP_EOL;
echo '== attributes ==', PHP_EOL;
$x = fresh();
$at = $x->attributes();
var_dump(isset($at[-1]));
var_dump($at[-1]);
$at[-1] = 'z';
echo state($x), PHP_EOL;
?>
--EXPECTF--
== element list ==
bool(false)
NULL
Warning: main(): Cannot add element item number -1 when only 3 such elements exist in %s on line %d
<r a="1" b="2"><item>a</item><item>b</item><item>c</item></r>
<r a="1" b="2"><item>a</item><item>b</item><item>c</item></r>
== single element (SXE_ITER_NONE) ==
bool(false)
NULL
Warning: main(): Cannot add element item number -1 when only 0 such %d
<r a="1" b="2"><item>a</item><item>b</item><item>c</item></r>
Warning: main(): Cannot add element item number 5 when only 0 such elements exist in %s on line %d
<r a="1" b="2"><item>a</item><item>b</item><item>c</item></r>
<r a="1" b="2"><item>a</item><item>b</item><item>c</item></r>
== nested write ==
ValueError: Cannot use a negative offset
<r a="1" b="2"><item>a</item><item>b</item><item>c</item></r>
== attributes ==
bool(false)
NULL |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
sxe_get_element_by_offset scans with nodendx <= offset, which is already false on the first iteration for a negative offset, so it returns the node it was given.
$xml->item[-1]therefore reports isset() true and reads the first item. Assigning to it overwrites$xml->item[0]. Negative offsets now miss, and writing to one warns like an out-of-range positive offset instead of creating an element.