diff --git a/src/Tokenizer/Tokens.php b/src/Tokenizer/Tokens.php index edb321167f5..b3d8d34cf1d 100644 --- a/src/Tokenizer/Tokens.php +++ b/src/Tokenizer/Tokens.php @@ -340,10 +340,15 @@ public function clearEmptyTokens(): void for ($count = $index; $index < $limit; ++$index) { if (!$this->isEmptyAt($index)) { - $this[$count++] = $this[$index]; // @phpstan-ignore-line as we know that index exists + // use directly for speed, skip the register of token kinds found etc. + parent::offsetSet($count++, $this[$index]); } } + // we are moving the tokens, we need to clear the indices Cache + $this->blockStartCache = []; + $this->blockEndCache = []; + $this->setSize($count); } diff --git a/tests/Tokenizer/TokensTest.php b/tests/Tokenizer/TokensTest.php index 202a9220b00..330a84440a8 100644 --- a/tests/Tokenizer/TokensTest.php +++ b/tests/Tokenizer/TokensTest.php @@ -16,6 +16,7 @@ use PhpCsFixer\Tests\Test\Assert\AssertTokensTrait; use PhpCsFixer\Tests\TestCase; +use PhpCsFixer\Tokenizer\CT; use PhpCsFixer\Tokenizer\Token; use PhpCsFixer\Tokenizer\Tokens; @@ -1531,6 +1532,71 @@ public function provideInsertSlicesCases(): iterable ]; } + public function testBlockEdgeCachingOffsetSet(): void + { + $tokens = $this->getBlockEdgeCachingTestTokens(); + + $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, 5); + static::assertSame(9, $endIndex); + + $tokens->offsetSet(5, new Token('(')); + $tokens->offsetSet(9, new Token('(')); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Invalid param $startIndex - not a proper block "start".'); + + $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, 5); + } + + public function testBlockEdgeCachingClearAt(): void + { + $tokens = $this->getBlockEdgeCachingTestTokens(); + + $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, 5); + static::assertSame(9, $endIndex); + + $tokens->clearAt(7); // note: offsetUnset doesn't work here + $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, 5); + static::assertSame(9, $endIndex); + + $tokens->clearEmptyTokens(); + $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, 5); + static::assertSame(8, $endIndex); + } + + public function testBlockEdgeCachingInsertSlices(): void + { + $tokens = $this->getBlockEdgeCachingTestTokens(); + + $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, 5); + static::assertSame(9, $endIndex); + + $tokens->insertSlices([6 => [new Token([T_COMMENT, '/* A */'])], new Token([T_COMMENT, '/* B */'])]); + + $endIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_ARRAY_SQUARE_BRACE, 5); + static::assertSame(11, $endIndex); + } + + private function getBlockEdgeCachingTestTokens(): Tokens + { + Tokens::clearCache(); + + return Tokens::fromArray([ + new Token([T_OPEN_TAG, '