Added support for namespaced tags;

Simplified @see and @link parsing;
Added comments to the regexes of @method, @example and @source;
Added a test case for what 2812eac046 intended to fix.
This commit is contained in:
Vasil Rangelov
2012-11-22 00:37:22 +02:00
parent b20c7da00b
commit 7a03741e0b
8 changed files with 77 additions and 23 deletions
+15 -7
View File
@@ -113,16 +113,21 @@ class Tag implements \Reflector
); );
} }
$handler = __CLASS__;
if (isset(self::$tagHandlerMappings[$matches[1]])) { if (isset(self::$tagHandlerMappings[$matches[1]])) {
$handler = self::$tagHandlerMappings[$matches[1]]; $handler = self::$tagHandlerMappings[$matches[1]];
return new $handler( } elseif (isset($docblock)) {
$matches[1], $tagName = (string)new Type\Collection(
isset($matches[2]) ? $matches[2] : '', array($matches[1]),
$docblock, $docblock->getContext()
$location
); );
if (isset(self::$tagHandlerMappings[$tagName])) {
$handler = self::$tagHandlerMappings[$tagName];
} }
return new self( }
return new $handler(
$matches[1], $matches[1],
isset($matches[2]) ? $matches[2] : '', isset($matches[2]) ? $matches[2] : '',
$docblock, $docblock,
@@ -136,7 +141,9 @@ class Tag implements \Reflector
* Registers a handler for tags. The class specified is autoloaded if it's * Registers a handler for tags. The class specified is autoloaded if it's
* not available. It must inherit from this class. * not available. It must inherit from this class.
* *
* @param string $tag Name of tag to regiser a handler for. * @param string $tag Name of tag to regiser a handler for. When
* registering a namespaced tag, the full name, along with a prefixing
* slash MUST be provided.
* @param string|null $handler FQCN of handler. Specifing NULL removes the * @param string|null $handler FQCN of handler. Specifing NULL removes the
* handler for the specified tag, if any. * handler for the specified tag, if any.
* *
@@ -154,6 +161,7 @@ class Tag implements \Reflector
if ('' !== $tag if ('' !== $tag
&& class_exists($handler, true) && class_exists($handler, true)
&& is_subclass_of($handler, __CLASS__) && is_subclass_of($handler, __CLASS__)
&& !strpos($tag, '\\') //Accept no slash, and 1st slash at offset 0.
) { ) {
self::$tagHandlerMappings[$tag] = $handler; self::$tagHandlerMappings[$tag] = $handler;
return true; return true;
@@ -43,7 +43,17 @@ class ExampleTag extends SourceTag
) { ) {
Tag::__construct($type, $content, $docblock, $location); Tag::__construct($type, $content, $docblock, $location);
if (preg_match( if (preg_match(
'/^(?:\"([^\"]+)\"|(\S+))(?:\s+(.*))?$/su', '/^
(?:
# File path in quotes
\"([^\"]+)\"
|
# File URI
(\S+)
)
# Remaining content (parsed by SourceTag)
(?:\s+(.*))?
$/sux',
$this->description, $this->description,
$matches $matches
)) { )) {
@@ -42,17 +42,12 @@ class LinkTag extends Tag
Location $location = null Location $location = null
) { ) {
parent::__construct($type, $content, $docblock, $location); parent::__construct($type, $content, $docblock, $location);
$pieces = explode(' ', $this->description); $content = preg_split('/\s+/u', $this->description, 2);
if (count($pieces) > 1) { // any output is considered a type
$this->link = array_shift($pieces); $this->link = $content[0];
$this->description = implode(' ', $pieces);
} else {
$this->link = $content;
$this->description = $content;
}
$this->content = $content; $this->description = isset($content[1]) ? $content[1] : $content[0];
} }
/** /**
@@ -57,8 +57,24 @@ class MethodTag extends ReturnTag
// until a ) and whitespace : as method name with signature // until a ) and whitespace : as method name with signature
// 5. any remaining text : as description // 5. any remaining text : as description
if (preg_match( if (preg_match(
'/^[\s]*(?:([\w\|_\\\\]+)[\s]+)?(?:[\w_]+\(\)[\s]+)?([\w\|_\\\\]+)' '/^
.'\(([^\)]*)\)[\s]*(.*)/u', # Return type
(?:
([\w\|_\\\\]+)
\s+
)?
# Legacy method name (not captured)
(?:
[\w_]+\(\)\s+
)?
# Method name
([\w\|_\\\\]+)
# Arguments
\(([^\)]*)\)
\s*
# Description
(.*)
$/sux',
$this->description, $this->description,
$matches $matches
)) { )) {
@@ -42,12 +42,12 @@ class SeeTag extends Tag
Location $location = null Location $location = null
) { ) {
parent::__construct($type, $content, $docblock, $location); parent::__construct($type, $content, $docblock, $location);
$content = preg_split('/\s+/u', $content); $content = preg_split('/\s+/u', $this->description, 2);
// any output is considered a type // any output is considered a type
$this->refers = array_shift($content); $this->refers = $content[0];
$this->description = implode(' ', $content); $this->description = isset($content[1]) ? $content[1] : '';
} }
/** /**
@@ -51,7 +51,18 @@ class SourceTag extends Tag
) { ) {
parent::__construct($type, $content, $docblock, $location); parent::__construct($type, $content, $docblock, $location);
if (preg_match( if (preg_match(
'/^([1-9]\d*)\s*(?:([1-9]\d*)\s+)?(.*)$/su', '/^
# Starting line
([1-9]\d*)
\s*
# Number of lines
(?:
((?1))
\s+
)?
# Description
(.*)
$/sux',
$this->description, $this->description,
$matches $matches
)) { )) {
@@ -92,6 +92,13 @@ class ReturnTagTest extends \PHPUnit_Framework_TestCase
'int', 'int',
array('int'), array('int'),
"Number of Bobs" "Number of Bobs"
),
array(
'return',
"int\nNumber of Bobs",
'int',
array('int'),
"Number of Bobs"
) )
); );
} }
@@ -90,6 +90,13 @@ class ThrowsTagTest extends \PHPUnit_Framework_TestCase
'int', 'int',
array('int'), array('int'),
"Number of Bobs" "Number of Bobs"
),
array(
'throws',
"int\nNumber of Bobs",
'int',
array('int'),
"Number of Bobs"
) )
); );
} }