Skip to content

Commit 216cf00

Browse files
committed
Add example on writing your own Tag and prepare for TagFactory objects
1 parent a6ffa93 commit 216cf00

26 files changed

+246
-24
lines changed

examples/03-reconstituting-a-docblock.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
require_once(__DIR__ . '/../vendor/autoload.php');
34

45
use phpDocumentor\Reflection\DocBlock\Serializer;
@@ -18,6 +19,9 @@
1819
$factory = DocBlockFactory::createInstance();
1920
$docblock = $factory->create($docComment);
2021

21-
$serializer = new Serializer();
22+
// Create the serializer that will reconstitute the DocBlock back to its original form.
23+
$serializer = new Serializer();
24+
25+
// Reconstitution is performed by the `getDocComment()` method.
2226
$reconstitutedDocComment = $serializer->getDocComment($docblock);
2327

examples/04-adding-your-own-tag.php

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
<?php
2+
/**
3+
* In this example we demonstrate how you can add your own Tag using a Static Factory method in your Tag class.
4+
*/
5+
6+
require_once(__DIR__ . '/../vendor/autoload.php');
7+
8+
use phpDocumentor\Reflection\DocBlock\Serializer;
9+
use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
10+
use phpDocumentor\Reflection\DocBlockFactory;
11+
use phpDocumentor\Reflection\DocBlock\Description;
12+
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
13+
use phpDocumentor\Reflection\DocBlock\Tags\BaseTag;
14+
use phpDocumentor\Reflection\Types\Context;
15+
use Webmozart\Assert\Assert;
16+
17+
/**
18+
* An example of a custom tag called `my-tag` with an optional description.
19+
*
20+
* A Custom Tag is a class that can consist of two parts:
21+
*
22+
* 1. a method `create` that is a static factory for this class.
23+
* 2. methods and properties that have this object act as an immutable Value Object representing a Tag instance.
24+
*
25+
* The static factory `create` is used to convert a tag line (without the tag name) into an instance of the
26+
* same tag object with the right constructor parameters set. This method has a dynamic list of parameters so that you
27+
* can inject various dependencies, see the method's DocBlock for more information.
28+
*
29+
* An object of this class, and its methods and properties, represent a single instance of that tag in your
30+
* documentation in the form of a Value Object whose properties should not be changed after instantiation (it should be
31+
* immutable).
32+
*
33+
* > Important: Tag classes that act as Factories using the `create` method should implement the TagFactory interface.
34+
*/
35+
final class MyTag extends BaseTag implements StaticMethod
36+
{
37+
/**
38+
* A required property that is used by Formatters to reconstitute the complete tag line.
39+
*
40+
* @see Formatter
41+
*
42+
* @var string
43+
*/
44+
protected $name = 'my-tag';
45+
46+
/**
47+
* The constructor for this Tag; this should contain all properties for this object.
48+
*
49+
* @param Description $description An example of how to add a Description to the tag; the Description is often
50+
* an optional variable so passing null is allowed in this instance (though you can
51+
* also construct an empty description object).
52+
*
53+
* @see BaseTag for the declaration of the description property and getDescription method.
54+
*/
55+
public function __construct(Description $description = null)
56+
{
57+
$this->description = $description;
58+
}
59+
60+
/**
61+
* A static Factory that creates a new instance of the current Tag.
62+
*
63+
* In this example the MyTag tag can be created by passing a description text as $body. Because we have added
64+
* a $descriptionFactory that is type-hinted as DescriptionFactory we can now construct a new Description object
65+
* and pass that to the constructor.
66+
*
67+
* > You could directly instantiate a Description object here but that won't be parsed for inline tags and Types
68+
* > won't be resolved. The DescriptionFactory will take care of those actions.
69+
*
70+
* The `create` method's interface states that this method only features a single parameter (`$body`) but the
71+
* {@see TagFactory} will read the signature of this method and if it has more parameters then it will try
72+
* to find declarations for it in the ServiceLocator of the TagFactory (see {@see TagFactory::$serviceLocator}).
73+
*
74+
* > Important: all properties following the `$body` should default to `null`, otherwise PHP will error because
75+
* > it no longer matches the interface. This is why you often see the default tags check that an optional argument
76+
* > is not null nonetheless.
77+
*
78+
* @param string $body
79+
* @param DescriptionFactory $descriptionFactory
80+
* @param Context|null $context The Context is used to resolve Types and FQSENs, although optional
81+
* it is highly recommended to pass it. If you omit it then it is assumed that
82+
* the DocBlock is in the global namespace and has no `use` statements.
83+
*
84+
* @see Tag for the interface declaration of the `create` method.
85+
* @see Tag::create() for more information on this method's workings.
86+
*
87+
* @return MyTag
88+
*/
89+
public static function create($body, DescriptionFactory $descriptionFactory = null, Context $context = null)
90+
{
91+
Assert::string($body);
92+
Assert::notNull($descriptionFactory);
93+
94+
return new static($descriptionFactory->create($body, $context));
95+
}
96+
97+
/**
98+
* Returns a rendition of the original tag line.
99+
*
100+
* This method is used to reconstitute a DocBlock into its original form by the {@see Serializer}. It should
101+
* feature all parts of the tag so that the serializer can put it back together.
102+
*
103+
* @return string
104+
*/
105+
public function __toString()
106+
{
107+
return (string)$this->description;
108+
}
109+
}
110+
111+
$docComment = <<<DOCCOMMENT
112+
/**
113+
* This is an example of a summary.
114+
*
115+
* @my-tag I have a description
116+
*/
117+
DOCCOMMENT;
118+
119+
// Make a mapping between the tag name `my-tag` and the Tag class containing the Factory Method `create`.
120+
$customTags = ['my-tag' => MyTag::class];
121+
122+
// Do pass the list of custom tags to the Factory for the DocBlockFactory.
123+
$factory = DocBlockFactory::createInstance($customTags);
124+
// You can also add Tags later using `$factory->registerTagHandler()` with a tag name and Tag class name.
125+
126+
// Create the DocBlock
127+
$docblock = $factory->create($docComment);
128+
129+
// Take a look: the $customTagObjects now contain an array with your newly added tag
130+
$customTagObjects = $docblock->getTagsByName('my-tag');
131+
132+
// As an experiment: let's reconstitute the DocBlock and observe that because we added a __toString() method
133+
// to the tag class that we can now also see it.
134+
$serializer = new Serializer();
135+
$reconstitutedDocComment = $serializer->getDocComment($docblock);

src/DocBlock/Serializer.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ private function addAsterisksForEachLine($indent, $text)
109109
*/
110110
private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength)
111111
{
112-
$text = $docblock->getSummary() . "\n\n" . $docblock->getDescription();
112+
$text = $docblock->getSummary() . ((string)$docblock->getDescription() ? "\n\n" . $docblock->getDescription()
113+
: '');
113114
if ($wrapLength !== null) {
114115
$text = wordwrap($text, $wrapLength);
115116
return $text;

src/DocBlock/StandardTagFactory.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
namespace phpDocumentor\Reflection\DocBlock;
1414

15+
use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
1516
use phpDocumentor\Reflection\DocBlock\Tags\Generic;
1617
use phpDocumentor\Reflection\FqsenResolver;
1718
use phpDocumentor\Reflection\Types\Context;
@@ -139,7 +140,7 @@ public function registerTagHandler($tagName, $handler)
139140
Assert::stringNotEmpty($tagName);
140141
Assert::stringNotEmpty($handler);
141142
Assert::classExists($handler);
142-
Assert::implementsInterface($handler, Tag::class);
143+
Assert::implementsInterface($handler, StaticMethod::class);
143144

144145
if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
145146
throw new \InvalidArgumentException(

src/DocBlock/Tags/Author.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
/**
1818
* Reflection class for an {@}author tag in a Docblock.
1919
*/
20-
final class Author extends BaseTag
20+
final class Author extends BaseTag implements Factory\StaticMethod
2121
{
2222
/** @var string register that this is the author tag. */
2323
protected $name = 'author';

src/DocBlock/Tags/Covers.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
/**
2323
* Reflection class for a @covers tag in a Docblock.
2424
*/
25-
final class Covers extends BaseTag
25+
final class Covers extends BaseTag implements Factory\StaticMethod
2626
{
2727
protected $name = 'covers';
2828

src/DocBlock/Tags/Deprecated.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
/**
2121
* Reflection class for a {@}deprecated tag in a Docblock.
2222
*/
23-
final class Deprecated extends BaseTag
23+
final class Deprecated extends BaseTag implements Factory\StaticMethod
2424
{
2525
protected $name = 'deprecated';
2626

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
/**
3+
* This file is part of phpDocumentor.
4+
*
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*
8+
* @copyright 2010-2015 Mike van Riel<[email protected]>
9+
* @license http://www.opensource.org/licenses/mit-license.php MIT
10+
* @link http://phpdoc.org
11+
*/
12+
13+
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
14+
15+
interface StaticMethod
16+
{
17+
public static function create($body);
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
/**
3+
* This file is part of phpDocumentor.
4+
*
5+
* For the full copyright and license information, please view the LICENSE
6+
* file that was distributed with this source code.
7+
*
8+
* @copyright 2010-2015 Mike van Riel<[email protected]>
9+
* @license http://www.opensource.org/licenses/mit-license.php MIT
10+
* @link http://phpdoc.org
11+
*/
12+
13+
namespace phpDocumentor\Reflection\DocBlock\Tags\Factory;
14+
15+
interface Strategy
16+
{
17+
public function create($body);
18+
}

src/DocBlock/Tags/Generic.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
/**
2222
* Parses a tag definition for a DocBlock.
2323
*/
24-
class Generic extends BaseTag
24+
class Generic extends BaseTag implements Factory\StaticMethod
2525
{
2626
/**
2727
* Parses a tag and populates the member variables.

0 commit comments

Comments
 (0)