From ed7b7919e3b3051daca7328a767510545da99406 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Sun, 9 Feb 2020 08:52:20 +0100 Subject: [PATCH] Limit characters after tag name Previously we allowed all characters after a tag name which made it a bit fuzzy how tags are handled. Psr-5 is more strict about the characters that are allowed in tags and those that are part of the body. A tag name can now be followed by `(`, and `{` all other characters are forbidden. The first character of the body is not restricted anymore. fixes #165 --- src/DocBlock/StandardTagFactory.php | 11 +-- .../unit/DocBlock/StandardTagFactoryTest.php | 87 +++++++++++++++++++ 2 files changed, 90 insertions(+), 8 deletions(-) diff --git a/src/DocBlock/StandardTagFactory.php b/src/DocBlock/StandardTagFactory.php index 61cba18..2ed5be5 100644 --- a/src/DocBlock/StandardTagFactory.php +++ b/src/DocBlock/StandardTagFactory.php @@ -46,6 +46,7 @@ use function count; use function get_class; use function preg_match; use function strpos; +use function trim; /** * Creates a Tag object given the contents of a tag. @@ -147,13 +148,7 @@ final class StandardTagFactory implements TagFactory [$tagName, $tagBody] = $this->extractTagParts($tagLine); - if ($tagBody !== '' && strpos($tagBody, '[') === 0) { - throw new InvalidArgumentException( - 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' - ); - } - - return $this->createTag($tagBody, $tagName, $context); + return $this->createTag(trim($tagBody), $tagName, $context); } /** @@ -199,7 +194,7 @@ final class StandardTagFactory implements TagFactory private function extractTagParts(string $tagLine) : array { $matches = []; - if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')(?:\s*([^\s].*)|$)/us', $tagLine, $matches)) { + if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')((?:[\s\(\{:])\s*([^\s].*)|$)/us', $tagLine, $matches)) { throw new InvalidArgumentException( 'The tag "' . $tagLine . '" does not seem to be wellformed, please check it for errors' ); diff --git a/tests/unit/DocBlock/StandardTagFactoryTest.php b/tests/unit/DocBlock/StandardTagFactoryTest.php index 9235344..cd6b99a 100644 --- a/tests/unit/DocBlock/StandardTagFactoryTest.php +++ b/tests/unit/DocBlock/StandardTagFactoryTest.php @@ -13,6 +13,7 @@ declare(strict_types=1); namespace phpDocumentor\Reflection\DocBlock; +use InvalidArgumentException; use Mockery as m; use phpDocumentor\Reflection\DocBlock\Tags\Author; use phpDocumentor\Reflection\DocBlock\Tags\Formatter; @@ -341,4 +342,90 @@ class StandardTagFactoryTest extends TestCase $this->assertInstanceOf(InvalidTag::class, $tag); } + + /** + * @dataProvider validTagProvider + */ + public function testValidFormattedTags(string $input, string $tagName, string $render) : void + { + $fqsenResolver = $this->prophesize(FqsenResolver::class); + $tagFactory = new StandardTagFactory($fqsenResolver->reveal()); + $tagFactory->registerTagHandler('tag', Generic::class); + $tag = $tagFactory->create($input); + + self::assertSame($tagName, $tag->getName()); + self::assertSame($render, $tag->render()); + } + + /** + * @return string[][] + * + * @phpstan-return array> + */ + public function validTagProvider() : array + { + //rendered result is adding a space, because the tags are not rendered properly. + return [ + 'tag without body' => [ + '@tag', + 'tag', + '@tag', + ], + 'tag specialization' => [ + '@tag:some-spec', + 'tag', + '@tag :some-spec', + ], + 'tag with textual description' => [ + '@tag some text', + 'tag', + '@tag some text', + ], + 'tag [a]' => [ + '@tag [is valid]', + 'tag', + '@tag [is valid]', + ], + 'tag {a}' => [ + '@tag {is valid}', + 'tag', + '@tag {is valid}', + ], + 'tag{a}' => [ + '@tag{is valid}', + 'tag', + '@tag {is valid}', + ], + 'tag(a)' => [ + '@tag(is valid)', + 'tag', + '@tag (is valid)', + ], + ]; + } + + /** + * @dataProvider invalidTagProvider + */ + public function testInValidFormattedTags(string $input) : void + { + $this->expectException(InvalidArgumentException::class); + $fqsenResolver = $this->prophesize(FqsenResolver::class); + $tagFactory = new StandardTagFactory($fqsenResolver->reveal()); + $tagFactory->registerTagHandler('tag', Generic::class); + $tagFactory->create($input); + } + + /** + * @return string[][] + * + * @phpstan-return list> + */ + public function invalidTagProvider() : array + { + return [ + ['@tag[invalid]'], + ['@tag@invalid'], + ]; + } }