From 216cf0025f09ab5c9eb6f65e0967777548da649b Mon Sep 17 00:00:00 2001 From: Mike van Riel Date: Sun, 28 Jun 2015 11:56:16 +0200 Subject: [PATCH] Add example on writing your own Tag and prepare for TagFactory objects --- examples/03-reconstituting-a-docblock.php | 6 +- examples/04-adding-your-own-tag.php | 135 +++++++++++++++++++++ src/DocBlock/Serializer.php | 3 +- src/DocBlock/StandardTagFactory.php | 3 +- src/DocBlock/Tags/Author.php | 2 +- src/DocBlock/Tags/Covers.php | 2 +- src/DocBlock/Tags/Deprecated.php | 2 +- src/DocBlock/Tags/Factory/StaticMethod.php | 18 +++ src/DocBlock/Tags/Factory/Strategy.php | 18 +++ src/DocBlock/Tags/Generic.php | 2 +- src/DocBlock/Tags/Link.php | 2 +- src/DocBlock/Tags/Method.php | 2 +- src/DocBlock/Tags/Param.php | 2 +- src/DocBlock/Tags/Property.php | 2 +- src/DocBlock/Tags/PropertyRead.php | 2 +- src/DocBlock/Tags/PropertyWrite.php | 2 +- src/DocBlock/Tags/Return_.php | 2 +- src/DocBlock/Tags/See.php | 2 +- src/DocBlock/Tags/Since.php | 2 +- src/DocBlock/Tags/Source.php | 2 +- src/DocBlock/Tags/Throws.php | 2 +- src/DocBlock/Tags/Uses.php | 2 +- src/DocBlock/Tags/Var_.php | 2 +- src/DocBlock/Tags/Version.php | 2 +- src/DocBlockFactory.php | 12 +- tests/integration/UsingTagsTest.php | 39 ++++++ 26 files changed, 246 insertions(+), 24 deletions(-) create mode 100644 examples/04-adding-your-own-tag.php create mode 100644 src/DocBlock/Tags/Factory/StaticMethod.php create mode 100644 src/DocBlock/Tags/Factory/Strategy.php create mode 100644 tests/integration/UsingTagsTest.php diff --git a/examples/03-reconstituting-a-docblock.php b/examples/03-reconstituting-a-docblock.php index 70c2c66..6bc10ba 100644 --- a/examples/03-reconstituting-a-docblock.php +++ b/examples/03-reconstituting-a-docblock.php @@ -1,4 +1,5 @@ create($docComment); -$serializer = new Serializer(); +// Create the serializer that will reconstitute the DocBlock back to its original form. +$serializer = new Serializer(); + +// Reconstitution is performed by the `getDocComment()` method. $reconstitutedDocComment = $serializer->getDocComment($docblock); diff --git a/examples/04-adding-your-own-tag.php b/examples/04-adding-your-own-tag.php new file mode 100644 index 0000000..026d606 --- /dev/null +++ b/examples/04-adding-your-own-tag.php @@ -0,0 +1,135 @@ + Important: Tag classes that act as Factories using the `create` method should implement the TagFactory interface. + */ +final class MyTag extends BaseTag implements StaticMethod +{ + /** + * A required property that is used by Formatters to reconstitute the complete tag line. + * + * @see Formatter + * + * @var string + */ + protected $name = 'my-tag'; + + /** + * The constructor for this Tag; this should contain all properties for this object. + * + * @param Description $description An example of how to add a Description to the tag; the Description is often + * an optional variable so passing null is allowed in this instance (though you can + * also construct an empty description object). + * + * @see BaseTag for the declaration of the description property and getDescription method. + */ + public function __construct(Description $description = null) + { + $this->description = $description; + } + + /** + * A static Factory that creates a new instance of the current Tag. + * + * In this example the MyTag tag can be created by passing a description text as $body. Because we have added + * a $descriptionFactory that is type-hinted as DescriptionFactory we can now construct a new Description object + * and pass that to the constructor. + * + * > You could directly instantiate a Description object here but that won't be parsed for inline tags and Types + * > won't be resolved. The DescriptionFactory will take care of those actions. + * + * The `create` method's interface states that this method only features a single parameter (`$body`) but the + * {@see TagFactory} will read the signature of this method and if it has more parameters then it will try + * to find declarations for it in the ServiceLocator of the TagFactory (see {@see TagFactory::$serviceLocator}). + * + * > Important: all properties following the `$body` should default to `null`, otherwise PHP will error because + * > it no longer matches the interface. This is why you often see the default tags check that an optional argument + * > is not null nonetheless. + * + * @param string $body + * @param DescriptionFactory $descriptionFactory + * @param Context|null $context The Context is used to resolve Types and FQSENs, although optional + * it is highly recommended to pass it. If you omit it then it is assumed that + * the DocBlock is in the global namespace and has no `use` statements. + * + * @see Tag for the interface declaration of the `create` method. + * @see Tag::create() for more information on this method's workings. + * + * @return MyTag + */ + public static function create($body, DescriptionFactory $descriptionFactory = null, Context $context = null) + { + Assert::string($body); + Assert::notNull($descriptionFactory); + + return new static($descriptionFactory->create($body, $context)); + } + + /** + * Returns a rendition of the original tag line. + * + * This method is used to reconstitute a DocBlock into its original form by the {@see Serializer}. It should + * feature all parts of the tag so that the serializer can put it back together. + * + * @return string + */ + public function __toString() + { + return (string)$this->description; + } +} + +$docComment = << MyTag::class]; + +// Do pass the list of custom tags to the Factory for the DocBlockFactory. +$factory = DocBlockFactory::createInstance($customTags); +// You can also add Tags later using `$factory->registerTagHandler()` with a tag name and Tag class name. + +// Create the DocBlock +$docblock = $factory->create($docComment); + +// Take a look: the $customTagObjects now contain an array with your newly added tag +$customTagObjects = $docblock->getTagsByName('my-tag'); + +// As an experiment: let's reconstitute the DocBlock and observe that because we added a __toString() method +// to the tag class that we can now also see it. +$serializer = new Serializer(); +$reconstitutedDocComment = $serializer->getDocComment($docblock); diff --git a/src/DocBlock/Serializer.php b/src/DocBlock/Serializer.php index c42d722..7f1c89d 100644 --- a/src/DocBlock/Serializer.php +++ b/src/DocBlock/Serializer.php @@ -109,7 +109,8 @@ class Serializer */ private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength) { - $text = $docblock->getSummary() . "\n\n" . $docblock->getDescription(); + $text = $docblock->getSummary() . ((string)$docblock->getDescription() ? "\n\n" . $docblock->getDescription() + : ''); if ($wrapLength !== null) { $text = wordwrap($text, $wrapLength); return $text; diff --git a/src/DocBlock/StandardTagFactory.php b/src/DocBlock/StandardTagFactory.php index 5b68e40..dc90bf4 100644 --- a/src/DocBlock/StandardTagFactory.php +++ b/src/DocBlock/StandardTagFactory.php @@ -12,6 +12,7 @@ namespace phpDocumentor\Reflection\DocBlock; +use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod; use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\Types\Context; @@ -139,7 +140,7 @@ final class StandardTagFactory implements TagFactory Assert::stringNotEmpty($tagName); Assert::stringNotEmpty($handler); Assert::classExists($handler); - Assert::implementsInterface($handler, Tag::class); + Assert::implementsInterface($handler, StaticMethod::class); if (strpos($tagName, '\\') && $tagName[0] !== '\\') { throw new \InvalidArgumentException( diff --git a/src/DocBlock/Tags/Author.php b/src/DocBlock/Tags/Author.php index 02482d6..41a2788 100644 --- a/src/DocBlock/Tags/Author.php +++ b/src/DocBlock/Tags/Author.php @@ -17,7 +17,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for an {@}author tag in a Docblock. */ -final class Author extends BaseTag +final class Author extends BaseTag implements Factory\StaticMethod { /** @var string register that this is the author tag. */ protected $name = 'author'; diff --git a/src/DocBlock/Tags/Covers.php b/src/DocBlock/Tags/Covers.php index a9a4284..0465a6a 100644 --- a/src/DocBlock/Tags/Covers.php +++ b/src/DocBlock/Tags/Covers.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a @covers tag in a Docblock. */ -final class Covers extends BaseTag +final class Covers extends BaseTag implements Factory\StaticMethod { protected $name = 'covers'; diff --git a/src/DocBlock/Tags/Deprecated.php b/src/DocBlock/Tags/Deprecated.php index 9d7004f..ab4fb41 100644 --- a/src/DocBlock/Tags/Deprecated.php +++ b/src/DocBlock/Tags/Deprecated.php @@ -20,7 +20,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}deprecated tag in a Docblock. */ -final class Deprecated extends BaseTag +final class Deprecated extends BaseTag implements Factory\StaticMethod { protected $name = 'deprecated'; diff --git a/src/DocBlock/Tags/Factory/StaticMethod.php b/src/DocBlock/Tags/Factory/StaticMethod.php new file mode 100644 index 0000000..98aea45 --- /dev/null +++ b/src/DocBlock/Tags/Factory/StaticMethod.php @@ -0,0 +1,18 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; + +interface StaticMethod +{ + public static function create($body); +} diff --git a/src/DocBlock/Tags/Factory/Strategy.php b/src/DocBlock/Tags/Factory/Strategy.php new file mode 100644 index 0000000..b9ca0b8 --- /dev/null +++ b/src/DocBlock/Tags/Factory/Strategy.php @@ -0,0 +1,18 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection\DocBlock\Tags\Factory; + +interface Strategy +{ + public function create($body); +} diff --git a/src/DocBlock/Tags/Generic.php b/src/DocBlock/Tags/Generic.php index 73e7e86..25c4856 100644 --- a/src/DocBlock/Tags/Generic.php +++ b/src/DocBlock/Tags/Generic.php @@ -21,7 +21,7 @@ use Webmozart\Assert\Assert; /** * Parses a tag definition for a DocBlock. */ -class Generic extends BaseTag +class Generic extends BaseTag implements Factory\StaticMethod { /** * Parses a tag and populates the member variables. diff --git a/src/DocBlock/Tags/Link.php b/src/DocBlock/Tags/Link.php index 75f057b..ccc0e2d 100644 --- a/src/DocBlock/Tags/Link.php +++ b/src/DocBlock/Tags/Link.php @@ -20,7 +20,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a @link tag in a Docblock. */ -final class Link extends BaseTag +final class Link extends BaseTag implements Factory\StaticMethod { protected $name = 'link'; diff --git a/src/DocBlock/Tags/Method.php b/src/DocBlock/Tags/Method.php index d49152d..c0c9fa5 100644 --- a/src/DocBlock/Tags/Method.php +++ b/src/DocBlock/Tags/Method.php @@ -23,7 +23,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for an {@}method in a Docblock. */ -final class Method extends BaseTag +final class Method extends BaseTag implements Factory\StaticMethod { protected $name = 'method'; diff --git a/src/DocBlock/Tags/Param.php b/src/DocBlock/Tags/Param.php index 8ae6360..7a94e73 100644 --- a/src/DocBlock/Tags/Param.php +++ b/src/DocBlock/Tags/Param.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for the {@}param tag in a Docblock. */ -final class Param extends BaseTag +final class Param extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'param'; diff --git a/src/DocBlock/Tags/Property.php b/src/DocBlock/Tags/Property.php index 95e474a..23205e1 100644 --- a/src/DocBlock/Tags/Property.php +++ b/src/DocBlock/Tags/Property.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}property tag in a Docblock. */ -class Property extends BaseTag +class Property extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'property'; diff --git a/src/DocBlock/Tags/PropertyRead.php b/src/DocBlock/Tags/PropertyRead.php index bd16889..1711357 100644 --- a/src/DocBlock/Tags/PropertyRead.php +++ b/src/DocBlock/Tags/PropertyRead.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}property-read tag in a Docblock. */ -class PropertyRead extends BaseTag +class PropertyRead extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'property-read'; diff --git a/src/DocBlock/Tags/PropertyWrite.php b/src/DocBlock/Tags/PropertyWrite.php index 4f6b598..acf2ea9 100644 --- a/src/DocBlock/Tags/PropertyWrite.php +++ b/src/DocBlock/Tags/PropertyWrite.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}property-write tag in a Docblock. */ -class PropertyWrite extends BaseTag +class PropertyWrite extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'property-write'; diff --git a/src/DocBlock/Tags/Return_.php b/src/DocBlock/Tags/Return_.php index 31e0c70..58e0eac 100644 --- a/src/DocBlock/Tags/Return_.php +++ b/src/DocBlock/Tags/Return_.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}return tag in a Docblock. */ -final class Return_ extends BaseTag +final class Return_ extends BaseTag implements Factory\StaticMethod { protected $name = 'return'; diff --git a/src/DocBlock/Tags/See.php b/src/DocBlock/Tags/See.php index d3c446a..709ad1f 100644 --- a/src/DocBlock/Tags/See.php +++ b/src/DocBlock/Tags/See.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for an {@}see tag in a Docblock. */ -class See extends BaseTag +class See extends BaseTag implements Factory\StaticMethod { protected $name = 'see'; diff --git a/src/DocBlock/Tags/Since.php b/src/DocBlock/Tags/Since.php index 4d0b274..37f4876 100644 --- a/src/DocBlock/Tags/Since.php +++ b/src/DocBlock/Tags/Since.php @@ -20,7 +20,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}since tag in a Docblock. */ -final class Since extends BaseTag +final class Since extends BaseTag implements Factory\StaticMethod { protected $name = 'since'; diff --git a/src/DocBlock/Tags/Source.php b/src/DocBlock/Tags/Source.php index 72c84e4..39f44ba 100644 --- a/src/DocBlock/Tags/Source.php +++ b/src/DocBlock/Tags/Source.php @@ -20,7 +20,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}source tag in a Docblock. */ -final class Source extends BaseTag +final class Source extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'source'; diff --git a/src/DocBlock/Tags/Throws.php b/src/DocBlock/Tags/Throws.php index f9a2564..5984ddb 100644 --- a/src/DocBlock/Tags/Throws.php +++ b/src/DocBlock/Tags/Throws.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}throws tag in a Docblock. */ -final class Throws extends BaseTag +final class Throws extends BaseTag implements Factory\StaticMethod { protected $name = 'throws'; diff --git a/src/DocBlock/Tags/Uses.php b/src/DocBlock/Tags/Uses.php index ad1749f..0b15cab 100644 --- a/src/DocBlock/Tags/Uses.php +++ b/src/DocBlock/Tags/Uses.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}uses tag in a Docblock. */ -final class Uses extends BaseTag +final class Uses extends BaseTag implements Factory\StaticMethod { protected $name = 'uses'; diff --git a/src/DocBlock/Tags/Var_.php b/src/DocBlock/Tags/Var_.php index 055f288..bc1b592 100644 --- a/src/DocBlock/Tags/Var_.php +++ b/src/DocBlock/Tags/Var_.php @@ -22,7 +22,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}var tag in a Docblock. */ -class Var_ extends BaseTag +class Var_ extends BaseTag implements Factory\StaticMethod { /** @var string */ protected $name = 'var'; diff --git a/src/DocBlock/Tags/Version.php b/src/DocBlock/Tags/Version.php index af2b3d9..9da8896 100644 --- a/src/DocBlock/Tags/Version.php +++ b/src/DocBlock/Tags/Version.php @@ -20,7 +20,7 @@ use Webmozart\Assert\Assert; /** * Reflection class for a {@}version tag in a Docblock. */ -final class Version extends BaseTag +final class Version extends BaseTag implements Factory\StaticMethod { protected $name = 'version'; diff --git a/src/DocBlockFactory.php b/src/DocBlockFactory.php index c402cab..a9d24b9 100644 --- a/src/DocBlockFactory.php +++ b/src/DocBlockFactory.php @@ -54,11 +54,12 @@ final class DocBlockFactory implements DocBlockFactoryInterface $tagFactory->addService($descriptionFactory); $tagFactory->addService(new TypeResolver($fqsenResolver)); - foreach ($additionalTags as $tagName => $tagClassName) { - $tagFactory->registerTagHandler($tagName, $tagClassName); + $docBlockFactory = new self($descriptionFactory, $tagFactory); + foreach ($additionalTags as $tagName => $tagHandler) { + $docBlockFactory->registerTagHandler($tagName, $tagHandler); } - return new self($descriptionFactory, $tagFactory); + return $docBlockFactory; } /** @@ -100,6 +101,11 @@ final class DocBlockFactory implements DocBlockFactoryInterface ); } + public function registerTagHandler($tagName, $handler) + { + $this->tagFactory->registerTagHandler($tagName, $handler); + } + /** * Strips the asterisks from the DocBlock comment. * diff --git a/tests/integration/UsingTagsTest.php b/tests/integration/UsingTagsTest.php new file mode 100644 index 0000000..984811b --- /dev/null +++ b/tests/integration/UsingTagsTest.php @@ -0,0 +1,39 @@ + + * @license http://www.opensource.org/licenses/mit-license.php MIT + * @link http://phpdoc.org + */ + +namespace phpDocumentor\Reflection; + +use phpDocumentor\Reflection\DocBlock\Description; +use phpDocumentor\Reflection\DocBlock\StandardTagFactory; +use phpDocumentor\Reflection\DocBlock\Tag; +use phpDocumentor\Reflection\DocBlock\Tags\See; + +/** + * @coversNothing + */ +class UsingTagsTest extends \PHPUnit_Framework_TestCase +{ + public function testAddingYourOwnTagUsingAStaticMethodAsFactory() + { + /** + * @var object[] $customTagObjects + * @var string $docComment + * @var string $reconstitutedDocComment + */ + include(__DIR__ . '/../../examples/04-adding-your-own-tag.php'); + + $this->assertInstanceOf(\MyTag::class, $customTagObjects[0]); + $this->assertSame('my-tag', $customTagObjects[0]->getName()); + $this->assertSame('I have a description', (string)$customTagObjects[0]->getDescription()); + $this->assertSame($docComment, $reconstitutedDocComment); + } +}