From ed7b7919e3b3051daca7328a767510545da99406 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Sun, 9 Feb 2020 08:52:20 +0100 Subject: [PATCH 1/3] 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'], + ]; + } } From 264bd1fff7f4679a81e41bcb959b6587c73af844 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Sun, 9 Feb 2020 09:05:22 +0100 Subject: [PATCH 2/3] Handle tag specialization correctly --- src/DocBlock/StandardTagFactory.php | 4 ++-- tests/unit/DocBlock/StandardTagFactoryTest.php | 11 ++++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/DocBlock/StandardTagFactory.php b/src/DocBlock/StandardTagFactory.php index 2ed5be5..7b348c7 100644 --- a/src/DocBlock/StandardTagFactory.php +++ b/src/DocBlock/StandardTagFactory.php @@ -68,7 +68,7 @@ use function trim; final class StandardTagFactory implements TagFactory { /** PCRE regular expression matching a tag name. */ - public const REGEX_TAGNAME = '[\w\-\_\\\\]+'; + public const REGEX_TAGNAME = '[\w\-\_\\\\:]+'; /** * @var string[] An array with a tag as a key, and an @@ -194,7 +194,7 @@ final class StandardTagFactory implements TagFactory private function extractTagParts(string $tagLine) : array { $matches = []; - if (!preg_match('/^@(' . self::REGEX_TAGNAME . ')((?:[\s\(\{:])\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 cd6b99a..8ca3e57 100644 --- a/tests/unit/DocBlock/StandardTagFactoryTest.php +++ b/tests/unit/DocBlock/StandardTagFactoryTest.php @@ -372,9 +372,14 @@ class StandardTagFactoryTest extends TestCase '@tag', ], 'tag specialization' => [ - '@tag:some-spec', - 'tag', - '@tag :some-spec', + '@tag:some-spec body', + 'tag:some-spec', + '@tag:some-spec body', + ], + 'tag specialization(a)' => [ + '@tag:some-spec(body)', + 'tag:some-spec', + '@tag:some-spec (body)', ], 'tag with textual description' => [ '@tag some text', From 9169749b9e57c74fe4b58188801cf87dbbe1fd69 Mon Sep 17 00:00:00 2001 From: Jaapio Date: Sun, 9 Feb 2020 09:11:55 +0100 Subject: [PATCH 3/3] Improve test descriptions --- tests/unit/DocBlock/StandardTagFactoryTest.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/unit/DocBlock/StandardTagFactoryTest.php b/tests/unit/DocBlock/StandardTagFactoryTest.php index 8ca3e57..3337204 100644 --- a/tests/unit/DocBlock/StandardTagFactoryTest.php +++ b/tests/unit/DocBlock/StandardTagFactoryTest.php @@ -376,7 +376,7 @@ class StandardTagFactoryTest extends TestCase 'tag:some-spec', '@tag:some-spec body', ], - 'tag specialization(a)' => [ + 'tag specialization followed by parenthesis' => [ '@tag:some-spec(body)', 'tag:some-spec', '@tag:some-spec (body)', @@ -386,22 +386,22 @@ class StandardTagFactoryTest extends TestCase 'tag', '@tag some text', ], - 'tag [a]' => [ + 'tag body starting with sqare brackets is allowed' => [ '@tag [is valid]', 'tag', '@tag [is valid]', ], - 'tag {a}' => [ + 'tag body starting with curly brackets is allowed' => [ '@tag {is valid}', 'tag', '@tag {is valid}', ], - 'tag{a}' => [ + 'tag name followed by curly brackets directly is allowed' => [ '@tag{is valid}', 'tag', '@tag {is valid}', ], - 'tag(a)' => [ + 'parenthesis directly following a tag name is valid' => [ '@tag(is valid)', 'tag', '@tag (is valid)',