From 1d684e95bba132c345abb08b6470ac3f8f551e38 Mon Sep 17 00:00:00 2001 From: lacatoire Date: Wed, 15 Apr 2026 22:05:33 +0200 Subject: [PATCH 1/2] Allow balanced braces inside inline tag descriptions The inline-tag tokenizer used in DescriptionFactory recognised nested inline tags and hanging '{', but not a bare balanced pair such as '{braces}' used as part of an inline tag body. Matching such a pair now preserves the surrounding inline tag instead of truncating it at the first closing brace. Refs phpDocumentor/ReflectionDocBlock#255 --- src/DocBlock/DescriptionFactory.php | 4 +++ .../unit/DocBlock/DescriptionFactoryTest.php | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/src/DocBlock/DescriptionFactory.php b/src/DocBlock/DescriptionFactory.php index 6915c16f..7067626d 100644 --- a/src/DocBlock/DescriptionFactory.php +++ b/src/DocBlock/DescriptionFactory.php @@ -115,6 +115,10 @@ private function lex(string $contents): array # Notice that this also matches "{}", as a way to later introduce it as an escape sequence. \{(?1)?\} | + # Match a balanced pair of braces that is not an inline tag, e.g. "{braces}" used as part + # of the description of a surrounding inline tag. + \{[^{}]*\} + | # Make sure we match hanging "{". \{ ) diff --git a/tests/unit/DocBlock/DescriptionFactoryTest.php b/tests/unit/DocBlock/DescriptionFactoryTest.php index 6b087ee9..189e7008 100644 --- a/tests/unit/DocBlock/DescriptionFactoryTest.php +++ b/tests/unit/DocBlock/DescriptionFactoryTest.php @@ -109,6 +109,33 @@ public function testDescriptionCanParseAStringWithInlineTag(): void * @covers ::__construct * @covers ::create */ + /** + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Link + * @uses \phpDocumentor\Reflection\DocBlock\Tags\BaseTag + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter + * @uses \phpDocumentor\Reflection\Types\Context + * + * @covers ::__construct + * @covers ::create + */ + public function testDescriptionCanParseStringWithInlineTagContainingBalancedBraces(): void + { + $contents = 'This description has a {@link http://phpdoc.org/ This contains {braces}} in it.'; + $context = new Context(''); + $tagFactory = m::mock(TagFactory::class); + $tagFactory->shouldReceive('create') + ->once() + ->with('@link http://phpdoc.org/ This contains {braces}', $context) + ->andReturn(new LinkTag('http://phpdoc.org/', new Description('This contains {braces}'))); + + $factory = new DescriptionFactory($tagFactory); + $description = $factory->create($contents, $context); + + $this->assertSame($contents, $description->render()); + $this->assertSame('This description has a %1$s in it.', $description->getBodyTemplate()); + } + public function testDescriptionCanParseAStringStartingWithInlineTag(): void { $contents = '{@link http://phpdoc.org/ This} is text for a description that starts with an inline tag.'; From d91fcbe8fa20f72d05a062d1d9d79f6c9a8cbd61 Mon Sep 17 00:00:00 2001 From: lacatoire Date: Wed, 15 Apr 2026 22:07:36 +0200 Subject: [PATCH 2/2] Restore docblock detached from testDescriptionCanParseAStringStartingWithInlineTag --- .../unit/DocBlock/DescriptionFactoryTest.php | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/tests/unit/DocBlock/DescriptionFactoryTest.php b/tests/unit/DocBlock/DescriptionFactoryTest.php index 189e7008..b9ece642 100644 --- a/tests/unit/DocBlock/DescriptionFactoryTest.php +++ b/tests/unit/DocBlock/DescriptionFactoryTest.php @@ -109,6 +109,22 @@ public function testDescriptionCanParseAStringWithInlineTag(): void * @covers ::__construct * @covers ::create */ + public function testDescriptionCanParseAStringStartingWithInlineTag(): void + { + $contents = '{@link http://phpdoc.org/ This} is text for a description that starts with an inline tag.'; + $context = new Context(''); + $tagFactory = m::mock(TagFactory::class); + $tagFactory->shouldReceive('create') + ->once() + ->with('@link http://phpdoc.org/ This', $context) + ->andReturn(new LinkTag('http://phpdoc.org/', new Description('This'))); + + $factory = new DescriptionFactory($tagFactory); + $description = $factory->create($contents, $context); + + $this->assertSame($contents, $description->render()); + } + /** * @uses \phpDocumentor\Reflection\DocBlock\Description * @uses \phpDocumentor\Reflection\DocBlock\Tags\Link @@ -136,22 +152,6 @@ public function testDescriptionCanParseStringWithInlineTagContainingBalancedBrac $this->assertSame('This description has a %1$s in it.', $description->getBodyTemplate()); } - public function testDescriptionCanParseAStringStartingWithInlineTag(): void - { - $contents = '{@link http://phpdoc.org/ This} is text for a description that starts with an inline tag.'; - $context = new Context(''); - $tagFactory = m::mock(TagFactory::class); - $tagFactory->shouldReceive('create') - ->once() - ->with('@link http://phpdoc.org/ This', $context) - ->andReturn(new LinkTag('http://phpdoc.org/', new Description('This'))); - - $factory = new DescriptionFactory($tagFactory); - $description = $factory->create($contents, $context); - - $this->assertSame($contents, $description->render()); - } - /** * @uses \phpDocumentor\Reflection\DocBlock\Description * @uses \phpDocumentor\Reflection\DocBlock\Tags\Link