Refactored Tag::createInstance() to use a simple tag-to-class map, that has the default tag handlers pre-registered.

This commit is contained in:
Vasil Rangelov
2012-11-11 00:54:51 +02:00
parent d73444f859
commit 08dc71ba37
2 changed files with 137 additions and 12 deletions
+62 -12
View File
@@ -39,6 +39,30 @@ class Tag implements \Reflector
/** @var \phpDocumentor\Reflection\DocBlock docblock class */ /** @var \phpDocumentor\Reflection\DocBlock docblock class */
protected $docblock; 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. * Factory method responsible for instantiating the correct sub type.
* *
@@ -48,7 +72,7 @@ class Tag implements \Reflector
* *
* @return \phpDocumentor\Reflection\DocBlock\Tag * @return \phpDocumentor\Reflection\DocBlock\Tag
*/ */
public static function createInstance($tag_line) final public static function createInstance($tag_line)
{ {
if (!preg_match( if (!preg_match(
'/^@([\w\-\_\\\\]+)(?:\s*([^\s].*)|$)?/us', '/^@([\w\-\_\\\\]+)(?:\s*([^\s].*)|$)?/us',
@@ -60,18 +84,44 @@ class Tag implements \Reflector
); );
} }
// support hypphen separated tag names if (isset(self::$tagHandlerMappings[$matches[1]])) {
$tag_name = str_replace( $handler = self::$tagHandlerMappings[$matches[1]];
' ', return new $handler(
'', $matches[1],
ucwords(str_replace('-', ' ', $matches[1])) isset($matches[2]) ? $matches[2] : ''
).'Tag'; );
$class_name = 'phpDocumentor\\Reflection\\DocBlock\\Tag\\' . $tag_name; }
return new self($matches[1], isset($matches[2]) ? $matches[2] : '');
}
return ($matches[1] === strtolower($matches[1]) /**
&& @class_exists($class_name)) * Registers a handler for tags.
? new $class_name($matches[1], isset($matches[2]) ? $matches[2] : '') *
: new self($matches[1], isset($matches[2]) ? $matches[2] : ''); * 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;
}
} }
/** /**
@@ -32,6 +32,81 @@ class TagTest extends \PHPUnit_Framework_TestCase
{ {
Tag::createInstance('Invalid tag line'); 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(<<<TAG_HANDLER
<?php
class MyVarHandler extends \phpDocumentor\Reflection\DocBlock\Tag {}
TAG_HANDLER
);
$this->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 * Test that the \phpDocumentor\Reflection\DocBlock\Tag\VarTag can
* understand the @var doc block. * understand the @var doc block.