Skip to content

Commit a8dc047

Browse files
authored
Merge pull request #200 from phpDocumentor/feature/improved-tag-parsing
Limit characters after tag name
2 parents 6a88d1a + 9169749 commit a8dc047

File tree

2 files changed

+96
-9
lines changed

2 files changed

+96
-9
lines changed

src/DocBlock/StandardTagFactory.php

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
use function get_class;
4747
use function preg_match;
4848
use function strpos;
49+
use function trim;
4950

5051
/**
5152
* Creates a Tag object given the contents of a tag.
@@ -67,7 +68,7 @@
6768
final class StandardTagFactory implements TagFactory
6869
{
6970
/** PCRE regular expression matching a tag name. */
70-
public const REGEX_TAGNAME = '[\w\-\_\\\\]+';
71+
public const REGEX_TAGNAME = '[\w\-\_\\\\:]+';
7172

7273
/**
7374
* @var string[] An array with a tag as a key, and an
@@ -147,13 +148,7 @@ public function create(string $tagLine, ?TypeContext $context = null) : Tag
147148

148149
[$tagName, $tagBody] = $this->extractTagParts($tagLine);
149150

150-
if ($tagBody !== '' && strpos($tagBody, '[') === 0) {
151-
throw new InvalidArgumentException(
152-
'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'
153-
);
154-
}
155-
156-
return $this->createTag($tagBody, $tagName, $context);
151+
return $this->createTag(trim($tagBody), $tagName, $context);
157152
}
158153

159154
/**
@@ -199,7 +194,7 @@ public function registerTagHandler(string $tagName, string $handler) : void
199194
private function extractTagParts(string $tagLine) : array
200195
{
201196
$matches = [];
202-
if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)/us', $tagLine, $matches)) {
197+
if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')((?:[\s\(\{])\s*([^\s].*)|$)/us', $tagLine, $matches)) {
203198
throw new InvalidArgumentException(
204199
'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors'
205200
);

tests/unit/DocBlock/StandardTagFactoryTest.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
namespace phpDocumentor\Reflection\DocBlock;
1515

16+
use InvalidArgumentException;
1617
use Mockery as m;
1718
use phpDocumentor\Reflection\DocBlock\Tags\Author;
1819
use phpDocumentor\Reflection\DocBlock\Tags\Formatter;
@@ -341,4 +342,95 @@ public function testInvalidTagIsReturnedOnFailure() : void
341342

342343
$this->assertInstanceOf(InvalidTag::class, $tag);
343344
}
345+
346+
/**
347+
* @dataProvider validTagProvider
348+
*/
349+
public function testValidFormattedTags(string $input, string $tagName, string $render) : void
350+
{
351+
$fqsenResolver = $this->prophesize(FqsenResolver::class);
352+
$tagFactory = new StandardTagFactory($fqsenResolver->reveal());
353+
$tagFactory->registerTagHandler('tag', Generic::class);
354+
$tag = $tagFactory->create($input);
355+
356+
self::assertSame($tagName, $tag->getName());
357+
self::assertSame($render, $tag->render());
358+
}
359+
360+
/**
361+
* @return string[][]
362+
*
363+
* @phpstan-return array<string, array<int, string>>
364+
*/
365+
public function validTagProvider() : array
366+
{
367+
//rendered result is adding a space, because the tags are not rendered properly.
368+
return [
369+
'tag without body' => [
370+
'@tag',
371+
'tag',
372+
'@tag',
373+
],
374+
'tag specialization' => [
375+
'@tag:some-spec body',
376+
'tag:some-spec',
377+
'@tag:some-spec body',
378+
],
379+
'tag specialization followed by parenthesis' => [
380+
'@tag:some-spec(body)',
381+
'tag:some-spec',
382+
'@tag:some-spec (body)',
383+
],
384+
'tag with textual description' => [
385+
'@tag some text',
386+
'tag',
387+
'@tag some text',
388+
],
389+
'tag body starting with sqare brackets is allowed' => [
390+
'@tag [is valid]',
391+
'tag',
392+
'@tag [is valid]',
393+
],
394+
'tag body starting with curly brackets is allowed' => [
395+
'@tag {is valid}',
396+
'tag',
397+
'@tag {is valid}',
398+
],
399+
'tag name followed by curly brackets directly is allowed' => [
400+
'@tag{is valid}',
401+
'tag',
402+
'@tag {is valid}',
403+
],
404+
'parenthesis directly following a tag name is valid' => [
405+
'@tag(is valid)',
406+
'tag',
407+
'@tag (is valid)',
408+
],
409+
];
410+
}
411+
412+
/**
413+
* @dataProvider invalidTagProvider
414+
*/
415+
public function testInValidFormattedTags(string $input) : void
416+
{
417+
$this->expectException(InvalidArgumentException::class);
418+
$fqsenResolver = $this->prophesize(FqsenResolver::class);
419+
$tagFactory = new StandardTagFactory($fqsenResolver->reveal());
420+
$tagFactory->registerTagHandler('tag', Generic::class);
421+
$tagFactory->create($input);
422+
}
423+
424+
/**
425+
* @return string[][]
426+
*
427+
* @phpstan-return list<array<int, string>>
428+
*/
429+
public function invalidTagProvider() : array
430+
{
431+
return [
432+
['@tag[invalid]'],
433+
['@tag@invalid'],
434+
];
435+
}
344436
}

0 commit comments

Comments
 (0)