Skip to content

Commit bc9ce73

Browse files
authored
Handle negative and overflowed offsets on TokensList. (#582)
* Handle negative and overflowed offsets on TokensList. * Fix PHPCS issue.
1 parent f46b2c8 commit bc9ce73

File tree

2 files changed

+45
-16
lines changed

2 files changed

+45
-16
lines changed

src/TokensList.php

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
use ArrayAccess;
88

9+
use function array_splice;
910
use function count;
1011
use function in_array;
1112
use function is_array;
@@ -213,25 +214,29 @@ public function getNextOfTypeAndFlag(int $type, int $flag): ?Token
213214
}
214215

215216
/**
216-
* Sets an value inside the container.
217+
* Sets a Token inside the list of tokens.
218+
* When defined, offset must be positive otherwise the offset is ignored.
219+
* If the offset is not defined (like in array_push) or if it is greater than the number of Tokens already stored,
220+
* the Token is appended to the list of tokens.
217221
*
218-
* @param int|null $offset the offset to be set
222+
* @param int|null $offset the offset to be set. Must be positive otherwise, nothing will be stored.
219223
* @param Token $value the token to be saved
220224
*
221225
* @return void
222226
*/
223227
#[\ReturnTypeWillChange]
224228
public function offsetSet($offset, $value)
225229
{
226-
if ($offset === null) {
230+
if ($offset === null || $offset >= $this->count) {
227231
$this->tokens[$this->count++] = $value;
228-
} else {
232+
} elseif ($offset >= 0) {
229233
$this->tokens[$offset] = $value;
230234
}
231235
}
232236

233237
/**
234-
* Gets a value from the container.
238+
* Gets a Token from the list of tokens.
239+
* If the offset is negative or above the number of tokens set in the list, will return null.
235240
*
236241
* @param int $offset the offset to be returned
237242
*
@@ -240,11 +245,12 @@ public function offsetSet($offset, $value)
240245
#[\ReturnTypeWillChange]
241246
public function offsetGet($offset)
242247
{
243-
return $offset < $this->count ? $this->tokens[$offset] : null;
248+
return $this->offsetExists($offset) ? $this->tokens[$offset] : null;
244249
}
245250

246251
/**
247252
* Checks if an offset was previously set.
253+
* If the offset is negative or above the number of tokens set in the list, will return false.
248254
*
249255
* @param int $offset the offset to be checked
250256
*
@@ -253,11 +259,11 @@ public function offsetGet($offset)
253259
#[\ReturnTypeWillChange]
254260
public function offsetExists($offset)
255261
{
256-
return $offset < $this->count;
262+
return $offset >= 0 && $offset < $this->count;
257263
}
258264

259265
/**
260-
* Unsets the value of an offset.
266+
* Unsets the value of an offset, if the offset exists.
261267
*
262268
* @param int $offset the offset to be unset
263269
*
@@ -266,12 +272,11 @@ public function offsetExists($offset)
266272
#[\ReturnTypeWillChange]
267273
public function offsetUnset($offset)
268274
{
269-
unset($this->tokens[$offset]);
270-
--$this->count;
271-
for ($i = $offset; $i < $this->count; ++$i) {
272-
$this->tokens[$i] = $this->tokens[$i + 1];
275+
if (! $this->offsetExists($offset)) {
276+
return;
273277
}
274278

275-
unset($this->tokens[$this->count]);
279+
array_splice($this->tokens, $offset, 1);
280+
--$this->count;
276281
}
277282
}

tests/Lexer/TokensListTest.php

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,17 +137,41 @@ public function testArrayAccess(): void
137137
// offsetSet($offset, $value)
138138
$list[2] = $this->tokens[2];
139139

140+
// offsetSet($offset, $value) with overflowed offset
141+
$list[$list->count] = $this->tokens[1];
142+
$this->assertSame($list[$list->count - 1], $this->tokens[1]);
143+
$this->assertSame($list->count, count($this->tokens) + 1);
144+
145+
// offsetGet($offset) with negative offset
146+
$this->assertNull($list[-1]);
147+
148+
// offsetGet($offset) with overflow offset
149+
$this->assertNull($list[$list->count]);
150+
140151
// offsetGet($offset)
141152
for ($i = 0, $count = count($this->tokens); $i < $count; ++$i) {
142-
$this->assertEquals($this->tokens[$i], $list[$i]);
153+
$this->assertSame($this->tokens[$i], $list[$i]);
143154
}
144155

145156
// offsetExists($offset)
146157
$this->assertArrayHasKey(2, $list);
147-
$this->assertArrayNotHasKey(13, $list);
158+
$this->assertArrayNotHasKey(14, $list);
159+
$this->assertArrayNotHasKey(-8, $list);
148160

149161
// offsetUnset($offset)
162+
$currentCountTokens = $list->count;
150163
unset($list[2]);
151-
$this->assertEquals($this->tokens[3], $list[2]);
164+
$newCountTokens = $list->count;
165+
$this->assertSame($this->tokens[3], $list[2]);
166+
$this->assertSame($currentCountTokens - 1, $newCountTokens);
167+
168+
// offsetUnset($offset) with invalid offset (negative or overflowed)
169+
$currentListTokens = $list->tokens;
170+
unset($list[-1]);
171+
$this->assertSame($currentListTokens, $list->tokens);
172+
$this->assertSame($newCountTokens, $list->count); // No unset actually performed.
173+
unset($list[999]);
174+
$this->assertSame($currentListTokens, $list->tokens);
175+
$this->assertSame($newCountTokens, $list->count); // No unset actually performed.
152176
}
153177
}

0 commit comments

Comments
 (0)