From 28df2a4cd6308236ae93cd9209432f3a18a419f4 Mon Sep 17 00:00:00 2001 From: Vasil Rangelov Date: Sun, 11 Nov 2012 00:54:51 +0200 Subject: [PATCH] Refactored Tag::createInstance() to use a simple tag-to-class map, that has the default tag handlers pre-registered. --- src/phpDocumentor/Reflection/DocBlock/Tag.php | 74 +++++++++++++++--- .../Reflection/DocBlock/TagTest.php | 75 +++++++++++++++++++ 2 files changed, 137 insertions(+), 12 deletions(-) diff --git a/src/phpDocumentor/Reflection/DocBlock/Tag.php b/src/phpDocumentor/Reflection/DocBlock/Tag.php index 2cdb680..4496904 100644 --- a/src/phpDocumentor/Reflection/DocBlock/Tag.php +++ b/src/phpDocumentor/Reflection/DocBlock/Tag.php @@ -38,6 +38,30 @@ class Tag implements \Reflector /** @var \phpDocumentor\Reflection\DocBlock docblock class */ protected $docblock; + + /** + * @var array An array with a tag as a key, and an FQCN to a class that + * handles it as an array value. The class is expected to inherit this + * class. + */ + private static $tagHandlerMappings = array( + 'author' => '\phpDocumentor\Reflection\DocBlock\Tag\AuthorTag', + 'covers' => '\phpDocumentor\Reflection\DocBlock\Tag\CoversTag', + 'link' => '\phpDocumentor\Reflection\DocBlock\Tag\LinkTag', + 'method' => '\phpDocumentor\Reflection\DocBlock\Tag\MethodTag', + 'param' => '\phpDocumentor\Reflection\DocBlock\Tag\ParamTag', + 'property-read' + => '\phpDocumentor\Reflection\DocBlock\Tag\PropertyReadTag', + 'property' => '\phpDocumentor\Reflection\DocBlock\Tag\PropertyTag', + 'property-write' + => '\phpDocumentor\Reflection\DocBlock\Tag\PropertyWriteTag', + 'return' => '\phpDocumentor\Reflection\DocBlock\Tag\ReturnTag', + 'see' => '\phpDocumentor\Reflection\DocBlock\Tag\SeeTag', + 'throw' => '\phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag', + 'throws' => '\phpDocumentor\Reflection\DocBlock\Tag\ThrowsTag', + 'uses' => '\phpDocumentor\Reflection\DocBlock\Tag\UsesTag', + 'var' => '\phpDocumentor\Reflection\DocBlock\Tag\VarTag' + ); /** * Factory method responsible for instantiating the correct sub type. @@ -48,7 +72,7 @@ class Tag implements \Reflector * * @return \phpDocumentor\Reflection\DocBlock\Tag */ - public static function createInstance($tag_line) + final public static function createInstance($tag_line) { if (!preg_match( '/^@([\w\-\_\\\\]+)(?:\s*([^\s].*)|$)?/us', @@ -60,18 +84,44 @@ class Tag implements \Reflector ); } - // support hypphen separated tag names - $tag_name = str_replace( - ' ', - '', - ucwords(str_replace('-', ' ', $matches[1])) - ).'Tag'; - $class_name = 'phpDocumentor\\Reflection\\DocBlock\\Tag\\' . $tag_name; + if (isset(self::$tagHandlerMappings[$matches[1]])) { + $handler = self::$tagHandlerMappings[$matches[1]]; + return new $handler( + $matches[1], + isset($matches[2]) ? $matches[2] : '' + ); + } + return new self($matches[1], isset($matches[2]) ? $matches[2] : ''); + } - return ($matches[1] === strtolower($matches[1]) - && @class_exists($class_name)) - ? new $class_name($matches[1], isset($matches[2]) ? $matches[2] : '') - : new self($matches[1], isset($matches[2]) ? $matches[2] : ''); + /** + * Registers a handler for tags. + * + * Registers a handler for tags. The class specified is autoloaded if it's + * not available. It must inherit from this class. + * + * @param string $tag Name of tag to regiser a handler for. + * @param string $handler FQCN of handler. Specifing NULL removes the + * handler for the specified tag, if any. + * + * @return bool TRUE on success, FALSE on failure. + */ + final public static function registerTagHandler($tag, $handler) + { + $tag = trim((string)$tag); + if (null === $handler) { + unset(self::$tagHandlerMappings[$tag]); + return true; + } + if ('' !== $tag + && class_exists($handler, true) + && is_subclass_of($handler, '\\' . __CLASS__) + ) { + self::$tagHandlerMappings[$tag] = $handler; + return true; + } else { + return false; + } } /** diff --git a/tests/phpDocumentor/Reflection/DocBlock/TagTest.php b/tests/phpDocumentor/Reflection/DocBlock/TagTest.php index 31a3fa9..9b5ee11 100644 --- a/tests/phpDocumentor/Reflection/DocBlock/TagTest.php +++ b/tests/phpDocumentor/Reflection/DocBlock/TagTest.php @@ -32,6 +32,81 @@ class TagTest extends \PHPUnit_Framework_TestCase { Tag::createInstance('Invalid tag line'); } + + public function testTagHandlerUnregistration() + { + $currentHandler = __NAMESPACE__ . '\Tag\VarTag'; + $tagPreUnreg = Tag::createInstance('@var mixed'); + $this->assertInstanceOf( + $currentHandler, + $tagPreUnreg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPreUnreg + ); + + Tag::registerTagHandler('var', null); + + $tagPostUnreg = Tag::createInstance('@var mixed'); + $this->assertNotInstanceOf( + $currentHandler, + $tagPostUnreg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPostUnreg + ); + + Tag::registerTagHandler('var', $currentHandler); + } + + public function testTagHandlerRegistration() + { + if (0 == ini_get('allow_url_include')) { + $this->markTestSkipped('"data" URIs for includes are required.'); + } + $currentHandler = __NAMESPACE__ . '\Tag\VarTag'; + $tagPreUnreg = Tag::createInstance('@var mixed'); + $this->assertInstanceOf( + $currentHandler, + $tagPreUnreg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPreUnreg + ); + + require 'data:text/plain;base64,'. base64_encode(<<assertFalse(Tag::registerTagHandler('var', 'Non existent')); + $this->assertTrue(Tag::registerTagHandler('var', '\MyVarHandler')); + $this->assertFalse( + Tag::registerTagHandler('var', __NAMESPACE__ . '\TagTest') + ); + + $tagPostUnreg = Tag::createInstance('@var mixed'); + $this->assertNotInstanceOf( + $currentHandler, + $tagPostUnreg + ); + $this->assertInstanceOf( + __NAMESPACE__ . '\Tag', + $tagPostUnreg + ); + $this->assertInstanceOf( + '\MyVarHandler', + $tagPostUnreg + ); + + $this->assertTrue(Tag::registerTagHandler('var', $currentHandler)); + } + /** * Test that the \phpDocumentor\Reflection\DocBlock\Tag\VarTag can * understand the @var doc block.