Add example on writing your own Tag and prepare for TagFactory objects

This commit is contained in:
Mike van Riel
2015-06-28 11:56:16 +02:00
parent a6ffa93e28
commit 216cf0025f
26 changed files with 246 additions and 24 deletions
+5 -1
View File
@@ -1,4 +1,5 @@
<?php <?php
require_once(__DIR__ . '/../vendor/autoload.php'); require_once(__DIR__ . '/../vendor/autoload.php');
use phpDocumentor\Reflection\DocBlock\Serializer; use phpDocumentor\Reflection\DocBlock\Serializer;
@@ -18,6 +19,9 @@ DOCCOMMENT;
$factory = DocBlockFactory::createInstance(); $factory = DocBlockFactory::createInstance();
$docblock = $factory->create($docComment); $docblock = $factory->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); $reconstitutedDocComment = $serializer->getDocComment($docblock);
+135
View File
@@ -0,0 +1,135 @@
<?php
/**
* In this example we demonstrate how you can add your own Tag using a Static Factory method in your Tag class.
*/
require_once(__DIR__ . '/../vendor/autoload.php');
use phpDocumentor\Reflection\DocBlock\Serializer;
use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
use phpDocumentor\Reflection\DocBlockFactory;
use phpDocumentor\Reflection\DocBlock\Description;
use phpDocumentor\Reflection\DocBlock\DescriptionFactory;
use phpDocumentor\Reflection\DocBlock\Tags\BaseTag;
use phpDocumentor\Reflection\Types\Context;
use Webmozart\Assert\Assert;
/**
* An example of a custom tag called `my-tag` with an optional description.
*
* A Custom Tag is a class that can consist of two parts:
*
* 1. a method `create` that is a static factory for this class.
* 2. methods and properties that have this object act as an immutable Value Object representing a Tag instance.
*
* The static factory `create` is used to convert a tag line (without the tag name) into an instance of the
* same tag object with the right constructor parameters set. This method has a dynamic list of parameters so that you
* can inject various dependencies, see the method's DocBlock for more information.
*
* An object of this class, and its methods and properties, represent a single instance of that tag in your
* documentation in the form of a Value Object whose properties should not be changed after instantiation (it should be
* immutable).
*
* > 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 = <<<DOCCOMMENT
/**
* This is an example of a summary.
*
* @my-tag I have a description
*/
DOCCOMMENT;
// Make a mapping between the tag name `my-tag` and the Tag class containing the Factory Method `create`.
$customTags = ['my-tag' => 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);
+2 -1
View File
@@ -109,7 +109,8 @@ class Serializer
*/ */
private function getSummaryAndDescriptionTextBlock(DocBlock $docblock, $wrapLength) 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) { if ($wrapLength !== null) {
$text = wordwrap($text, $wrapLength); $text = wordwrap($text, $wrapLength);
return $text; return $text;
+2 -1
View File
@@ -12,6 +12,7 @@
namespace phpDocumentor\Reflection\DocBlock; namespace phpDocumentor\Reflection\DocBlock;
use phpDocumentor\Reflection\DocBlock\Tags\Factory\StaticMethod;
use phpDocumentor\Reflection\DocBlock\Tags\Generic; use phpDocumentor\Reflection\DocBlock\Tags\Generic;
use phpDocumentor\Reflection\FqsenResolver; use phpDocumentor\Reflection\FqsenResolver;
use phpDocumentor\Reflection\Types\Context; use phpDocumentor\Reflection\Types\Context;
@@ -139,7 +140,7 @@ final class StandardTagFactory implements TagFactory
Assert::stringNotEmpty($tagName); Assert::stringNotEmpty($tagName);
Assert::stringNotEmpty($handler); Assert::stringNotEmpty($handler);
Assert::classExists($handler); Assert::classExists($handler);
Assert::implementsInterface($handler, Tag::class); Assert::implementsInterface($handler, StaticMethod::class);
if (strpos($tagName, '\\') && $tagName[0] !== '\\') { if (strpos($tagName, '\\') && $tagName[0] !== '\\') {
throw new \InvalidArgumentException( throw new \InvalidArgumentException(
+1 -1
View File
@@ -17,7 +17,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for an {@}author tag in a Docblock. * 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. */ /** @var string register that this is the author tag. */
protected $name = 'author'; protected $name = 'author';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a @covers tag in a Docblock. * 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'; protected $name = 'covers';
+1 -1
View File
@@ -20,7 +20,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}deprecated tag in a Docblock. * 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'; protected $name = 'deprecated';
@@ -0,0 +1,18 @@
<?php
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright 2010-2015 Mike van Riel<[email protected]>
* @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);
}
+18
View File
@@ -0,0 +1,18 @@
<?php
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright 2010-2015 Mike van Riel<[email protected]>
* @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);
}
+1 -1
View File
@@ -21,7 +21,7 @@ use Webmozart\Assert\Assert;
/** /**
* Parses a tag definition for a DocBlock. * 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. * Parses a tag and populates the member variables.
+1 -1
View File
@@ -20,7 +20,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a @link tag in a Docblock. * 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'; protected $name = 'link';
+1 -1
View File
@@ -23,7 +23,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for an {@}method in a Docblock. * Reflection class for an {@}method in a Docblock.
*/ */
final class Method extends BaseTag final class Method extends BaseTag implements Factory\StaticMethod
{ {
protected $name = 'method'; protected $name = 'method';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for the {@}param tag in a Docblock. * Reflection class for the {@}param tag in a Docblock.
*/ */
final class Param extends BaseTag final class Param extends BaseTag implements Factory\StaticMethod
{ {
/** @var string */ /** @var string */
protected $name = 'param'; protected $name = 'param';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}property tag in a Docblock. * Reflection class for a {@}property tag in a Docblock.
*/ */
class Property extends BaseTag class Property extends BaseTag implements Factory\StaticMethod
{ {
/** @var string */ /** @var string */
protected $name = 'property'; protected $name = 'property';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}property-read tag in a Docblock. * Reflection class for a {@}property-read tag in a Docblock.
*/ */
class PropertyRead extends BaseTag class PropertyRead extends BaseTag implements Factory\StaticMethod
{ {
/** @var string */ /** @var string */
protected $name = 'property-read'; protected $name = 'property-read';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}property-write tag in a Docblock. * Reflection class for a {@}property-write tag in a Docblock.
*/ */
class PropertyWrite extends BaseTag class PropertyWrite extends BaseTag implements Factory\StaticMethod
{ {
/** @var string */ /** @var string */
protected $name = 'property-write'; protected $name = 'property-write';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}return tag in a Docblock. * 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'; protected $name = 'return';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for an {@}see tag in a Docblock. * Reflection class for an {@}see tag in a Docblock.
*/ */
class See extends BaseTag class See extends BaseTag implements Factory\StaticMethod
{ {
protected $name = 'see'; protected $name = 'see';
+1 -1
View File
@@ -20,7 +20,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}since tag in a Docblock. * 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'; protected $name = 'since';
+1 -1
View File
@@ -20,7 +20,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}source tag in a Docblock. * Reflection class for a {@}source tag in a Docblock.
*/ */
final class Source extends BaseTag final class Source extends BaseTag implements Factory\StaticMethod
{ {
/** @var string */ /** @var string */
protected $name = 'source'; protected $name = 'source';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}throws tag in a Docblock. * 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'; protected $name = 'throws';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}uses tag in a Docblock. * 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'; protected $name = 'uses';
+1 -1
View File
@@ -22,7 +22,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}var tag in a Docblock. * Reflection class for a {@}var tag in a Docblock.
*/ */
class Var_ extends BaseTag class Var_ extends BaseTag implements Factory\StaticMethod
{ {
/** @var string */ /** @var string */
protected $name = 'var'; protected $name = 'var';
+1 -1
View File
@@ -20,7 +20,7 @@ use Webmozart\Assert\Assert;
/** /**
* Reflection class for a {@}version tag in a Docblock. * 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'; protected $name = 'version';
+9 -3
View File
@@ -54,11 +54,12 @@ final class DocBlockFactory implements DocBlockFactoryInterface
$tagFactory->addService($descriptionFactory); $tagFactory->addService($descriptionFactory);
$tagFactory->addService(new TypeResolver($fqsenResolver)); $tagFactory->addService(new TypeResolver($fqsenResolver));
foreach ($additionalTags as $tagName => $tagClassName) { $docBlockFactory = new self($descriptionFactory, $tagFactory);
$tagFactory->registerTagHandler($tagName, $tagClassName); 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. * Strips the asterisks from the DocBlock comment.
* *
+39
View File
@@ -0,0 +1,39 @@
<?php
/**
* This file is part of phpDocumentor.
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*
* @copyright 2010-2015 Mike van Riel<[email protected]>
* @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);
}
}