The reference implementation of the TypeLang PHPDoc
parser. It turns a raw /** ... */ comment into an immutable, iterable object
graph of its description and tags.
Full documentation is available at typelang.dev.
TypeLang PhpDoc Parser is available as a Composer package and can be installed using the following command in the root of your project:
composer require type-lang/phpdocuse TypeLang\PhpDoc\DocBlockParser;
$parser = new DocBlockParser();
$block = $parser->parse(<<<'PHPDOC'
/**
* Sends a notification to the given recipient.
*
* @see Mailer::send() The underlying transport.
* @link https://example.com/docs Delivery documentation.
* @return bool
*/
PHPDOC);
// The leading description (everything before the first tag)
echo $block->description;
// "Sends a notification to the given recipient."
// Tags form an ordered, countable, iterable collection
echo \count($block); // 3
foreach ($block as $tag) {
echo $tag->name; // "see", "link", "return"
}
// Known tags expose their parsed parts
$see = $block[0]; // SeeTag
echo $see->reference; // "Mailer::send()"A DocBlock is the representation of a whole comment: its description and its
tags.
/** |
* Hello world | β DocBlock's description.
* |
* @param int $example | β DocBlock's tag #1.
* @throws \Throwable Description | β DocBlock's tag #2.
*/ |
The object is immutable and additionally behaves as a collection of its tags, so it can be counted, iterated and accessed by offset:
/**
* DocBlock structure pseudocode (real impl may differ)
*
* @template-implements IteratorAggregate<array-key, TagInterface>
* @template-implements ArrayAccess<array-key, TagInterface>
*/
final readonly class DocBlock implements
IteratorAggregate,
ArrayAccess,
Countable
{
public ?DescriptionInterface $description;
/** @var list<TagInterface> */
public array $tags;
}A description is a DescriptionInterface implemented by one of two objects:
DescriptionInterface
β
βββββββββ΄ββββββββ
β β
Description TaggedDescription
A plain Description is just text:
/** |
* Hello world | β This is a simple description
βββββββββββ |
*/
/**
* Simple description structure pseudocode (real impl may differ)
*/
final readonly class Description implements DescriptionInterface
{
public string $value;
}A description may also contain inline tags. It is then represented as a
TaggedDescription, a composite of text fragments and nested tags:
/**
βββββββββββ | β This is a nested tag of the description.
* Hello world {@see some} and blah-blah-blah. |
βββββββββββ βββββββββββββββββββ | β These are plain text fragments.
*/
/**
* Tagged (composite) description structure pseudocode (real impl may differ)
*
* @template-implements IteratorAggregate<array-key, ComponentInterface>
* @template-implements ArrayAccess<array-key, ComponentInterface>
*/
final class TaggedDescription implements
DescriptionInterface,
IteratorAggregate,
ArrayAccess,
Countable
{
/** @var list<ComponentInterface> */
public array $components;
/** @var list<TagInterface> */
public array $tags;
}A Tag is a name (without the leading @) and its optional description.
/**
ββββββ | β This is a tag name.
* @throws \Throwable An error occurred. |
βββββββββββββββββββββββββββββ | β This is the tag suffix.
*/
/**
* Common tag structure pseudocode (real impl may differ)
*/
abstract class Tag implements TagInterface
{
public string $name;
public ?DescriptionInterface $description;
}Known tags extend Tag with their own parsed parts. For example, @see exposes
the referenced symbol or URI, while @link exposes its URI:
final class SeeTag extends Tag
{
public UriReference|CodeReference $reference;
}
final class LinkTag extends Tag
{
public UriReference $uri;
}An unregistered tag is returned as a generic Tag whose whole suffix becomes
its description.