diff --git a/src/Tokenizer/Tokens.php b/src/Tokenizer/Tokens.php index 2a4023836c0..c6550f4e121 100644 --- a/src/Tokenizer/Tokens.php +++ b/src/Tokenizer/Tokens.php @@ -48,7 +48,14 @@ class Tokens extends \SplFixedArray private static $cache = []; /** - * Cache of block edges. Any change in collection will invalidate it. + * Cache of block starts. Any change in collection will invalidate it. + * + * @var array + */ + private $blockStartCache = []; + + /** + * Cache of block ends. Any change in collection will invalidate it. * * @var array */ @@ -327,6 +334,7 @@ public function offsetUnset($index) */ public function offsetSet($index, $newval) { + $this->blockStartCache = []; $this->blockEndCache = []; if (!isset($this[$index]) || !$this[$index]->equals($newval)) { @@ -938,6 +946,7 @@ public function insertSlices(array $slices) $oldSize = \count($this); $this->changed = true; + $this->blockStartCache = []; $this->blockEndCache = []; $this->setSize($oldSize + $itemsCount); @@ -1436,8 +1445,13 @@ private function findOppositeBlockEdge($type, $searchIndex, $findEnd) throw new \InvalidArgumentException(sprintf('Invalid param type: "%s".', $type)); } - if (!self::isLegacyMode() && isset($this->blockEndCache[$searchIndex])) { - return $this->blockEndCache[$searchIndex]; + if (!self::isLegacyMode()) { + if ($findEnd && isset($this->blockStartCache[$searchIndex])) { + return $this->blockStartCache[$searchIndex]; + } + if (!$findEnd && isset($this->blockEndCache[$searchIndex])) { + return $this->blockEndCache[$searchIndex]; + } } $startEdge = $blockEdgeDefinitions[$type]['start']; @@ -1482,8 +1496,13 @@ private function findOppositeBlockEdge($type, $searchIndex, $findEnd) throw new \UnexpectedValueException(sprintf('Missing block "%s".', $findEnd ? 'end' : 'start')); } - $this->blockEndCache[$startIndex] = $index; - $this->blockEndCache[$index] = $startIndex; + if ($startIndex < $index) { + $this->blockStartCache[$startIndex] = $index; + $this->blockEndCache[$index] = $startIndex; + } else { + $this->blockStartCache[$index] = $startIndex; + $this->blockEndCache[$startIndex] = $index; + } return $index; } diff --git a/tests/Tokenizer/TokensTest.php b/tests/Tokenizer/TokensTest.php index eb94bde730a..2ca1b033210 100644 --- a/tests/Tokenizer/TokensTest.php +++ b/tests/Tokenizer/TokensTest.php @@ -827,6 +827,32 @@ public function testFindBlockEndLastParameterTrueDeprecated() static::assertSame(4, $tokens->findBlockEnd(Tokens::BLOCK_TYPE_DYNAMIC_VAR_BRACE, 2, true)); } + public function testFindBlockEndCalledMultipleTimes() + { + Tokens::clearCache(); + $tokens = Tokens::fromCode('findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, 2)); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessageMatches('/^Invalid param \$startIndex - not a proper block "start"\.$/'); + + $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, 7); + } + + public function testFindBlockStartEdgeCalledMultipleTimes() + { + Tokens::clearCache(); + $tokens = Tokens::fromCode('findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, 7)); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessageMatches('/^Invalid param \$startIndex - not a proper block "end"\.$/'); + + $tokens->findBlockStart(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, 2); + } + public function testEmptyTokens() { $code = '';