diff --git a/src/DocBlock/DescriptionFactory.php b/src/DocBlock/DescriptionFactory.php index 746a2c2..267e5af 100644 --- a/src/DocBlock/DescriptionFactory.php +++ b/src/DocBlock/DescriptionFactory.php @@ -69,10 +69,7 @@ class DescriptionFactory $tags = []; for ($i = 1; $i < $count; $i += 2) { - $tag = $this->tagFactory->create($tokens[$i], $context); - if ($tag !== null) { - $tags[] = $tag; - } + $tags[] = $this->tagFactory->create($tokens[$i], $context); $tokens[$i] = '%' . ++$tagCount . '$s'; } diff --git a/src/DocBlock/StandardTagFactory.php b/src/DocBlock/StandardTagFactory.php index 59caf8c..c7f5736 100644 --- a/src/DocBlock/StandardTagFactory.php +++ b/src/DocBlock/StandardTagFactory.php @@ -19,6 +19,7 @@ use phpDocumentor\Reflection\DocBlock\Tags\Covers; use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod; use phpDocumentor\Reflection\DocBlock\Tags\Generic; +use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; use phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag; use phpDocumentor\Reflection\DocBlock\Tags\Method; use phpDocumentor\Reflection\DocBlock\Tags\Param; @@ -138,7 +139,7 @@ final class StandardTagFactory implements TagFactory /** * {@inheritDoc} */ - public function create(string $tagLine, ?TypeContext $context = null) : ?Tag + public function create(string $tagLine, ?TypeContext $context = null) : Tag { if (!$context) { $context = new TypeContext(''); @@ -215,7 +216,7 @@ final class StandardTagFactory implements TagFactory * Creates a new tag object with the given name and body or returns null if the tag name was recognized but the * body was invalid. */ - private function createTag(string $body, string $name, TypeContext $context) : ?Tag + private function createTag(string $body, string $name, TypeContext $context) : Tag { $handlerClassName = $this->findHandlerClassName($name, $context); $arguments = $this->getArgumentsForParametersFromWiring( @@ -228,7 +229,7 @@ final class StandardTagFactory implements TagFactory $callable = [$handlerClassName, 'create']; return call_user_func_array($callable, $arguments); } catch (InvalidArgumentException $e) { - return null; + return InvalidTag::create($body, $name, $e); } } diff --git a/src/DocBlock/TagFactory.php b/src/DocBlock/TagFactory.php index e52e88d..1a2dc89 100644 --- a/src/DocBlock/TagFactory.php +++ b/src/DocBlock/TagFactory.php @@ -49,7 +49,7 @@ interface TagFactory * * @throws InvalidArgumentException If an invalid tag line was presented. */ - public function create(string $tagLine, ?TypeContext $context = null) : ?Tag; + public function create(string $tagLine, ?TypeContext $context = null) : Tag; /** * Registers a service with the Service Locator using the FQCN of the class or the alias, if provided. diff --git a/src/DocBlock/Tags/InvalidTag.php b/src/DocBlock/Tags/InvalidTag.php new file mode 100644 index 0000000..f098c55 --- /dev/null +++ b/src/DocBlock/Tags/InvalidTag.php @@ -0,0 +1,72 @@ +name = $name; + $this->body = $body; + $this->throwable = $throwable; + } + + public function getException() : Throwable + { + return $this->throwable; + } + + public function getName() : string + { + return $this->name; + } + + /** + * @inheritDoc + */ + public static function create(string $body, string $name = '', ?Throwable $exception = null) + { + Assert::notNull($exception); + + return new self($name, $body, $exception); + } + + public function render(?Formatter $formatter = null) : string + { + if ($formatter === null) { + $formatter = new Formatter\PassthroughFormatter(); + } + + return $formatter->format($this); + } + + public function __toString() : string + { + return $this->body; + } +} diff --git a/src/DocBlockFactory.php b/src/DocBlockFactory.php index 3055968..1e669b5 100644 --- a/src/DocBlockFactory.php +++ b/src/DocBlockFactory.php @@ -17,7 +17,6 @@ use InvalidArgumentException; use LogicException; use phpDocumentor\Reflection\DocBlock\DescriptionFactory; use phpDocumentor\Reflection\DocBlock\StandardTagFactory; -use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\TagFactory; use Webmozart\Assert\Assert; use function array_shift; @@ -236,12 +235,7 @@ final class DocBlockFactory implements DocBlockFactoryInterface $result = []; $lines = $this->splitTagBlockIntoTagLines($tags); foreach ($lines as $key => $tagLine) { - $tag = $this->tagFactory->create(trim($tagLine), $context); - if (!($tag instanceof Tag)) { - continue; - } - - $result[$key] = $tag; + $result[$key] = $this->tagFactory->create(trim($tagLine), $context); } return $result; diff --git a/tests/unit/DocBlock/DescriptionFactoryTest.php b/tests/unit/DocBlock/DescriptionFactoryTest.php index e10978e..3c4647f 100644 --- a/tests/unit/DocBlock/DescriptionFactoryTest.php +++ b/tests/unit/DocBlock/DescriptionFactoryTest.php @@ -13,7 +13,9 @@ declare(strict_types=1); namespace phpDocumentor\Reflection\DocBlock; +use Exception; use Mockery as m; +use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; use phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag; use phpDocumentor\Reflection\Types\Context; use PHPUnit\Framework\TestCase; @@ -162,6 +164,31 @@ DESCRIPTION; $this->assertSame($expectedDescription, $description->render()); } + /** + * @uses \phpDocumentor\Reflection\DocBlock\Description + * @uses \phpDocumentor\Reflection\DocBlock\Tags\InvalidTag + * @uses \phpDocumentor\Reflection\DocBlock\Tags\Formatter\PassthroughFormatter + * @uses \phpDocumentor\Reflection\Types\Context + * + * @covers ::__construct + * @covers ::create + */ + public function testDescriptionWithBrokenInlineTags() : void + { + $contents = 'This {@see $name} is a broken use case, but used in real life.'; + $context = new Context(''); + $tagFactory = m::mock(TagFactory::class); + $tagFactory->shouldReceive('create') + ->once() + ->with('@see $name', $context) + ->andReturn(InvalidTag::create('$name', 'see', new Exception())); + + $factory = new DescriptionFactory($tagFactory); + $description = $factory->create($contents, $context); + + $this->assertSame($contents, $description->render()); + } + /** * Provides a series of example strings that the parser should correctly interpret and return. * diff --git a/tests/unit/DocBlockFactoryTest.php b/tests/unit/DocBlockFactoryTest.php index 1c965ad..2298a01 100644 --- a/tests/unit/DocBlockFactoryTest.php +++ b/tests/unit/DocBlockFactoryTest.php @@ -279,37 +279,4 @@ DOCBLOCK $this->assertInstanceOf(DocBlock::class, $docblock); } - - /** - * @uses \phpDocumentor\Reflection\DocBlock\DescriptionFactory - * @uses \phpDocumentor\Reflection\DocBlock\Description - * - * @covers ::__construct - * @covers ::create - */ - public function testTagsAreFilteredForNullValues() : void - { - $tagString = << This is with - multiline description. -TAG; - - $tagFactory = m::mock(TagFactory::class); - $tagFactory->shouldReceive('create')->with($tagString, m::any())->andReturn(null); - - $fixture = new DocBlockFactory(new DescriptionFactory($tagFactory), $tagFactory); - - $given = << This is with - * multiline description. - */ -DOCBLOCK; - - $docblock = $fixture->create($given, new Context('')); - - $this->assertEquals([], $docblock->getTags()); - } }