diff --git a/src/DocBlock/StandardTagFactory.php b/src/DocBlock/StandardTagFactory.php index 61ad9be8..ecafe74f 100644 --- a/src/DocBlock/StandardTagFactory.php +++ b/src/DocBlock/StandardTagFactory.php @@ -13,45 +13,47 @@ namespace phpDocumentor\Reflection\DocBlock; +use function trim; +use function count; +use function strpos; +use function sprintf; +use ReflectionMethod; +use function get_class; +use function is_object; +use function preg_match; +use ReflectionNamedType; +use ReflectionParameter; +use function array_merge; +use function array_slice; +use Webmozart\Assert\Assert; use InvalidArgumentException; +use function array_key_exists; +use function call_user_func_array; +use phpDocumentor\Reflection\FqsenResolver; +use phpDocumentor\Reflection\DocBlock\Tags\Uses; +use phpDocumentor\Reflection\DocBlock\Tags\Var_; +use phpDocumentor\Reflection\DocBlock\Tags\Mixin; +use phpDocumentor\Reflection\DocBlock\Tags\Param; +use phpDocumentor\Reflection\DocBlock\Tags\Since; use phpDocumentor\Reflection\DocBlock\Tags\Author; use phpDocumentor\Reflection\DocBlock\Tags\Covers; -use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; -use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory; -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; +use phpDocumentor\Reflection\DocBlock\Tags\Source; +use phpDocumentor\Reflection\DocBlock\Tags\Throws; +use phpDocumentor\Reflection\DocBlock\Tags\Generic; + +use phpDocumentor\Reflection\DocBlock\Tags\Return_; +use phpDocumentor\Reflection\DocBlock\Tags\Version; use phpDocumentor\Reflection\DocBlock\Tags\Property; +use phpDocumentor\Reflection\DocBlock\Tags\Deprecated; +use phpDocumentor\Reflection\DocBlock\Tags\InvalidTag; use phpDocumentor\Reflection\DocBlock\Tags\PropertyRead; use phpDocumentor\Reflection\DocBlock\Tags\PropertyWrite; -use phpDocumentor\Reflection\DocBlock\Tags\Return_; use phpDocumentor\Reflection\DocBlock\Tags\See as SeeTag; -use phpDocumentor\Reflection\DocBlock\Tags\Since; -use phpDocumentor\Reflection\DocBlock\Tags\Source; -use phpDocumentor\Reflection\DocBlock\Tags\Throws; -use phpDocumentor\Reflection\DocBlock\Tags\Uses; -use phpDocumentor\Reflection\DocBlock\Tags\Var_; -use phpDocumentor\Reflection\DocBlock\Tags\Version; -use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\Types\Context as TypeContext; -use ReflectionMethod; -use ReflectionNamedType; -use ReflectionParameter; -use Webmozart\Assert\Assert; - -use function array_key_exists; -use function array_merge; -use function array_slice; -use function call_user_func_array; -use function count; -use function get_class; -use function is_object; -use function preg_match; -use function sprintf; -use function strpos; -use function trim; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory; +use phpDocumentor\Reflection\DocBlock\Tags\Link as LinkTag; +use phpDocumentor\Reflection\DocBlock\Tags\TemplateCovariant; /** * Creates a Tag object given the contents of a tag. @@ -80,25 +82,27 @@ final class StandardTagFactory implements TagFactory * FQCN to a class that handles it as an array value. */ private array $tagHandlerMappings = [ - 'author' => Author::class, - 'covers' => Covers::class, - 'deprecated' => Deprecated::class, - // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example', - 'link' => LinkTag::class, - 'method' => Method::class, - 'param' => Param::class, - 'property-read' => PropertyRead::class, - 'property' => Property::class, - 'property-write' => PropertyWrite::class, - 'return' => Return_::class, - 'see' => SeeTag::class, - 'since' => Since::class, - 'source' => Source::class, - 'throw' => Throws::class, - 'throws' => Throws::class, - 'uses' => Uses::class, - 'var' => Var_::class, - 'version' => Version::class, + 'author' => Author::class, + 'covers' => Covers::class, + 'deprecated' => Deprecated::class, + // 'example' => '\phpDocumentor\Reflection\DocBlock\Tags\Example', + 'link' => LinkTag::class, + 'mixin' => Mixin::class, + 'method' => Method::class, + 'param' => Param::class, + 'property-read' => PropertyRead::class, + 'property' => Property::class, + 'property-write' => PropertyWrite::class, + 'return' => Return_::class, + 'see' => SeeTag::class, + 'since' => Since::class, + 'source' => Source::class, + 'template-covariant' => TemplateCovariant::class, + 'throw' => Throws::class, + 'throws' => Throws::class, + 'uses' => Uses::class, + 'var' => Var_::class, + 'version' => Version::class, ]; /** diff --git a/src/DocBlock/Tags/Extends_.php b/src/DocBlock/Tags/Extends_.php new file mode 100644 index 00000000..7d54a889 --- /dev/null +++ b/src/DocBlock/Tags/Extends_.php @@ -0,0 +1,47 @@ +name = 'extends'; + $this->type = $type; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create(string $body) + { + Deprecation::trigger( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + return null; + } +} diff --git a/src/DocBlock/Tags/Factory/AbstractExtendsFactory.php b/src/DocBlock/Tags/Factory/AbstractExtendsFactory.php new file mode 100644 index 00000000..3325f2f5 --- /dev/null +++ b/src/DocBlock/Tags/Factory/AbstractExtendsFactory.php @@ -0,0 +1,32 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof ExtendsTagValueNode && $node->name === $this->tagName; + } +} diff --git a/src/DocBlock/Tags/Factory/AbstractImplementsFactory.php b/src/DocBlock/Tags/Factory/AbstractImplementsFactory.php new file mode 100644 index 00000000..45341cf1 --- /dev/null +++ b/src/DocBlock/Tags/Factory/AbstractImplementsFactory.php @@ -0,0 +1,32 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof ImplementsTagValueNode && $node->name === $this->tagName; + } +} diff --git a/src/DocBlock/Tags/Factory/ExtendsFactory.php b/src/DocBlock/Tags/Factory/ExtendsFactory.php new file mode 100644 index 00000000..37d36b0f --- /dev/null +++ b/src/DocBlock/Tags/Factory/ExtendsFactory.php @@ -0,0 +1,42 @@ +tagName = '@extends'; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, ExtendsTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Extends_( + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } +} diff --git a/src/DocBlock/Tags/Factory/ImplementsFactory.php b/src/DocBlock/Tags/Factory/ImplementsFactory.php new file mode 100644 index 00000000..e6266a23 --- /dev/null +++ b/src/DocBlock/Tags/Factory/ImplementsFactory.php @@ -0,0 +1,42 @@ +tagName = '@implements'; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, ImplementsTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Implements_( + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } +} diff --git a/src/DocBlock/Tags/Factory/TemplateExtendsFactory.php b/src/DocBlock/Tags/Factory/TemplateExtendsFactory.php new file mode 100644 index 00000000..97475f78 --- /dev/null +++ b/src/DocBlock/Tags/Factory/TemplateExtendsFactory.php @@ -0,0 +1,42 @@ +tagName = '@template-extends'; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, ExtendsTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new TemplateExtends( + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } +} diff --git a/src/DocBlock/Tags/Factory/TemplateFactory.php b/src/DocBlock/Tags/Factory/TemplateFactory.php new file mode 100644 index 00000000..3c1aafdf --- /dev/null +++ b/src/DocBlock/Tags/Factory/TemplateFactory.php @@ -0,0 +1,52 @@ +descriptionFactory = $descriptionFactory; + $this->typeResolver = $typeResolver; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, TemplateTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new Template( + $tagValue->name, + $this->typeResolver->createType($tagValue->bound, $context), + $this->typeResolver->createType($tagValue->default, $context), + $this->descriptionFactory->create($description, $context) + ); + } + + public function supports(PhpDocTagNode $node, Context $context): bool + { + return $node->value instanceof TemplateTagValueNode && $node->name === '@template'; + } +} diff --git a/src/DocBlock/Tags/Factory/TemplateImplementsFactory.php b/src/DocBlock/Tags/Factory/TemplateImplementsFactory.php new file mode 100644 index 00000000..0f69981f --- /dev/null +++ b/src/DocBlock/Tags/Factory/TemplateImplementsFactory.php @@ -0,0 +1,42 @@ +tagName = '@template-implements'; + } + + public function create(PhpDocTagNode $node, Context $context): Tag + { + $tagValue = $node->value; + Assert::isInstanceOf($tagValue, ImplementsTagValueNode::class); + + $description = $tagValue->getAttribute('description'); + if (is_string($description) === false) { + $description = $tagValue->description; + } + + return new TemplateImplements( + $this->typeResolver->createType($tagValue->type, $context), + $this->descriptionFactory->create($description, $context) + ); + } +} diff --git a/src/DocBlock/Tags/Implements_.php b/src/DocBlock/Tags/Implements_.php new file mode 100644 index 00000000..a871d438 --- /dev/null +++ b/src/DocBlock/Tags/Implements_.php @@ -0,0 +1,47 @@ +name = 'implements'; + $this->type = $type; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create(string $body) + { + Deprecation::trigger( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + return null; + } +} diff --git a/src/DocBlock/Tags/Mixin.php b/src/DocBlock/Tags/Mixin.php new file mode 100644 index 00000000..c15d30c1 --- /dev/null +++ b/src/DocBlock/Tags/Mixin.php @@ -0,0 +1,51 @@ +name = 'mixin'; + $this->type = $type; + $this->description = $description; + } + + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$type, $description] = self::extractTypeFromBody($body); + + $type = $typeResolver->resolve($type, $context); + $description = $descriptionFactory->create($description, $context); + + return new static($type, $description); + } +} diff --git a/src/DocBlock/Tags/Return_.php b/src/DocBlock/Tags/Return_.php index f130760a..7e9b0c7a 100644 --- a/src/DocBlock/Tags/Return_.php +++ b/src/DocBlock/Tags/Return_.php @@ -60,17 +60,4 @@ public static function create( return new static($type, $description); } - - public function __toString(): string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - - $type = $this->type ? '' . $this->type : 'mixed'; - - return $type . ($description !== '' ? ' ' . $description : ''); - } } diff --git a/src/DocBlock/Tags/TagWithType.php b/src/DocBlock/Tags/TagWithType.php index 271c41b5..89e29e5e 100644 --- a/src/DocBlock/Tags/TagWithType.php +++ b/src/DocBlock/Tags/TagWithType.php @@ -71,4 +71,17 @@ protected static function extractTypeFromBody(string $body): array return [$type, $description]; } + + public function __toString(): string + { + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + $type = (string) $this->type; + + return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : ''); + } } diff --git a/src/DocBlock/Tags/Template.php b/src/DocBlock/Tags/Template.php new file mode 100644 index 00000000..b5c7bc53 --- /dev/null +++ b/src/DocBlock/Tags/Template.php @@ -0,0 +1,87 @@ +name = 'template'; + $this->templateName = $templateName; + $this->bound = $bound; + $this->default = $default; + $this->description = $description; + } + + /** + * @deprecated Create using static factory is deprecated, + * this method should not be called directly by library consumers + */ + public static function create(string $body) + { + Deprecation::trigger( + 'phpdocumentor/reflection-docblock', + 'https://github.com/phpDocumentor/ReflectionDocBlock/issues/361', + 'Create using static factory is deprecated, this method should not be called directly + by library consumers', + ); + return null; + } + + public function getTemplateName(): string + { + return $this->templateName; + } + + public function getBound(): ?Type + { + return $this->bound; + } + + public function getDefault(): ?Type + { + return $this->default; + } + + public function __toString(): string + { + $bound = $this->bound !== null ? " of {$this->bound}" : ''; + $default = $this->default !== null ? " = {$this->default}" : ''; + + if ($this->description) { + $description = $this->description->render(); + } else { + $description = ''; + } + + return $this->templateName . $bound . $default . ($description !== '' ? ' ' . $description : ''); + } +} diff --git a/src/DocBlock/Tags/TemplateCovariant.php b/src/DocBlock/Tags/TemplateCovariant.php new file mode 100644 index 00000000..d5d50987 --- /dev/null +++ b/src/DocBlock/Tags/TemplateCovariant.php @@ -0,0 +1,51 @@ +name = 'template-covariant'; + $this->type = $type; + $this->description = $description; + } + + public static function create( + string $body, + ?TypeResolver $typeResolver = null, + ?DescriptionFactory $descriptionFactory = null, + ?TypeContext $context = null + ): self { + Assert::notNull($typeResolver); + Assert::notNull($descriptionFactory); + + [$type, $description] = self::extractTypeFromBody($body); + + $type = $typeResolver->resolve($type, $context); + $description = $descriptionFactory->create($description, $context); + + return new static($type, $description); + } +} diff --git a/src/DocBlock/Tags/TemplateExtends.php b/src/DocBlock/Tags/TemplateExtends.php new file mode 100644 index 00000000..4c9b2571 --- /dev/null +++ b/src/DocBlock/Tags/TemplateExtends.php @@ -0,0 +1,29 @@ +name = 'template-extends'; + } +} diff --git a/src/DocBlock/Tags/TemplateImplements.php b/src/DocBlock/Tags/TemplateImplements.php new file mode 100644 index 00000000..d6cd689e --- /dev/null +++ b/src/DocBlock/Tags/TemplateImplements.php @@ -0,0 +1,29 @@ +name = 'template-implements'; + } +} diff --git a/src/DocBlock/Tags/Throws.php b/src/DocBlock/Tags/Throws.php index f21c9101..e0818468 100644 --- a/src/DocBlock/Tags/Throws.php +++ b/src/DocBlock/Tags/Throws.php @@ -48,17 +48,4 @@ public static function create( return new static($type, $description); } - - public function __toString(): string - { - if ($this->description) { - $description = $this->description->render(); - } else { - $description = ''; - } - - $type = (string) $this->type; - - return $type . ($description !== '' ? ($type !== '' ? ' ' : '') . $description : ''); - } } diff --git a/src/DocBlockFactory.php b/src/DocBlockFactory.php index 1f7faf1a..408fbc32 100644 --- a/src/DocBlockFactory.php +++ b/src/DocBlockFactory.php @@ -13,34 +13,39 @@ namespace phpDocumentor\Reflection; -use InvalidArgumentException; use LogicException; -use phpDocumentor\Reflection\DocBlock\DescriptionFactory; -use phpDocumentor\Reflection\DocBlock\StandardTagFactory; +use InvalidArgumentException; +use Webmozart\Assert\Assert; use phpDocumentor\Reflection\DocBlock\Tag; use phpDocumentor\Reflection\DocBlock\TagFactory; -use phpDocumentor\Reflection\DocBlock\Tags\Factory\AbstractPHPStanFactory; +use phpDocumentor\Reflection\DocBlock\DescriptionFactory; +use phpDocumentor\Reflection\DocBlock\StandardTagFactory; use phpDocumentor\Reflection\DocBlock\Tags\Factory\Factory; -use phpDocumentor\Reflection\DocBlock\Tags\Factory\MethodFactory; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\VarFactory; use phpDocumentor\Reflection\DocBlock\Tags\Factory\ParamFactory; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\MethodFactory; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\ReturnFactory; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\ExtendsFactory; use phpDocumentor\Reflection\DocBlock\Tags\Factory\PropertyFactory; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\TemplateFactory; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\ImplementsFactory; use phpDocumentor\Reflection\DocBlock\Tags\Factory\PropertyReadFactory; use phpDocumentor\Reflection\DocBlock\Tags\Factory\PropertyWriteFactory; -use phpDocumentor\Reflection\DocBlock\Tags\Factory\ReturnFactory; -use phpDocumentor\Reflection\DocBlock\Tags\Factory\VarFactory; -use Webmozart\Assert\Assert; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\AbstractPHPStanFactory; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\TemplateExtendsFactory; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\TemplateImplementsFactory; -use function array_shift; +use function trim; use function count; +use function strpos; +use function substr; use function explode; use function is_object; -use function method_exists; use function preg_match; -use function preg_replace; +use function array_shift; use function str_replace; -use function strpos; -use function substr; -use function trim; +use function preg_replace; +use function method_exists; final class DocBlockFactory implements DocBlockFactoryInterface { @@ -76,7 +81,12 @@ public static function createInstance(array $additionalTags = []): DocBlockFacto new PropertyFactory($typeResolver, $descriptionFactory), new PropertyReadFactory($typeResolver, $descriptionFactory), new PropertyWriteFactory($typeResolver, $descriptionFactory), - new MethodFactory($typeResolver, $descriptionFactory) + new MethodFactory($typeResolver, $descriptionFactory), + new ImplementsFactory($typeResolver, $descriptionFactory), + new ExtendsFactory($typeResolver, $descriptionFactory), + new TemplateFactory($typeResolver, $descriptionFactory), + new TemplateImplementsFactory($typeResolver, $descriptionFactory), + new TemplateExtendsFactory($typeResolver, $descriptionFactory), ); $tagFactory->addService($descriptionFactory); @@ -88,6 +98,11 @@ public static function createInstance(array $additionalTags = []): DocBlockFacto $tagFactory->registerTagHandler('property-read', $phpstanTagFactory); $tagFactory->registerTagHandler('property-write', $phpstanTagFactory); $tagFactory->registerTagHandler('method', $phpstanTagFactory); + $tagFactory->registerTagHandler('extends', $phpstanTagFactory); + $tagFactory->registerTagHandler('implements', $phpstanTagFactory); + $tagFactory->registerTagHandler('template', $phpstanTagFactory); + $tagFactory->registerTagHandler('template-extends', $phpstanTagFactory); + $tagFactory->registerTagHandler('template-implements', $phpstanTagFactory); $docBlockFactory = new self($descriptionFactory, $tagFactory); foreach ($additionalTags as $tagName => $tagHandler) {