mirror of
https://github.com/barryvdh/ReflectionDocBlock.git
synced 2026-08-17 17:47:13 +00:00
Merge pull request #39 from mvriel/feature/phpDocumentor2/42
Add support for DocBlock template markers
This commit is contained in:
@@ -46,6 +46,12 @@ class DocBlock implements \Reflector
|
|||||||
/** @var Location Information about the location of this DocBlock. */
|
/** @var Location Information about the location of this DocBlock. */
|
||||||
protected $location = null;
|
protected $location = null;
|
||||||
|
|
||||||
|
/** @var bool Is this DocBlock (the start of) a template? */
|
||||||
|
protected $isTemplateStart = false;
|
||||||
|
|
||||||
|
/** @var bool Does this DocBlock signify the end of a DocBlock template? */
|
||||||
|
protected $isTemplateEnd = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses the given docblock and populates the member fields.
|
* Parses the given docblock and populates the member fields.
|
||||||
*
|
*
|
||||||
@@ -81,7 +87,9 @@ class DocBlock implements \Reflector
|
|||||||
|
|
||||||
$docblock = $this->cleanInput($docblock);
|
$docblock = $this->cleanInput($docblock);
|
||||||
|
|
||||||
list($short, $long, $tags) = $this->splitDocBlock($docblock);
|
list($templateMarker, $short, $long, $tags) = $this->splitDocBlock($docblock);
|
||||||
|
$this->isTemplateStart = $templateMarker === '#@+';
|
||||||
|
$this->isTemplateEnd = $templateMarker === '#@-';
|
||||||
$this->short_description = $short;
|
$this->short_description = $short;
|
||||||
$this->long_description = new DocBlock\Description($long, $this);
|
$this->long_description = new DocBlock\Description($long, $this);
|
||||||
$this->parseTags($tags);
|
$this->parseTags($tags);
|
||||||
@@ -119,74 +127,86 @@ class DocBlock implements \Reflector
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Splits the DocBlock into a short description, long description and
|
* Splits the DocBlock into a template marker, summary, description and block of tags.
|
||||||
* block of tags.
|
|
||||||
*
|
*
|
||||||
* @param string $comment Comment to split into the sub-parts.
|
* @param string $comment Comment to split into the sub-parts.
|
||||||
*
|
*
|
||||||
* @author RichardJ Special thanks to RichardJ for the regex responsible
|
* @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
|
||||||
* for the split.
|
* @author Mike van Riel <[email protected]> for extending the regex with template marker support.
|
||||||
*
|
*
|
||||||
* @return string[] containing the short-, long description and an element
|
* @return string[] containing the template marker (if any), summary, description and a string containing the tags.
|
||||||
* containing the tags.
|
|
||||||
*/
|
*/
|
||||||
protected function splitDocBlock($comment)
|
protected function splitDocBlock($comment)
|
||||||
{
|
{
|
||||||
|
// Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This
|
||||||
|
// method does not split tags so we return this verbatim as the fourth result (tags). This saves us the
|
||||||
|
// performance impact of running a regular expression
|
||||||
if (strpos($comment, '@') === 0) {
|
if (strpos($comment, '@') === 0) {
|
||||||
$matches = array('', '', $comment);
|
return array('', '', '', $comment);
|
||||||
} else {
|
}
|
||||||
// clears all extra horizontal whitespace from the line endings
|
|
||||||
// to prevent parsing issues
|
// clears all extra horizontal whitespace from the line endings to prevent parsing issues
|
||||||
$comment = preg_replace('/\h*$/Sum', '', $comment);
|
$comment = preg_replace('/\h*$/Sum', '', $comment);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Splits the docblock into a short description, long description and
|
* Splits the docblock into a template marker, short description, long description and tags section
|
||||||
* tags section
|
*
|
||||||
* - The short description is started from the first character until
|
* - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may
|
||||||
* a dot is encountered followed by a newline OR
|
* occur after it and will be stripped).
|
||||||
* two consecutive newlines (horizontal whitespace is taken into
|
* - The short description is started from the first character until a dot is encountered followed by a
|
||||||
* account to consider spacing errors)
|
* newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing
|
||||||
* - The long description, any character until a new line is
|
* errors). This is optional.
|
||||||
* encountered followed by an @ and word characters (a tag).
|
* - The long description, any character until a new line is encountered followed by an @ and word
|
||||||
* This is optional.
|
* characters (a tag). This is optional.
|
||||||
* - Tags; the remaining characters
|
* - Tags; the remaining characters
|
||||||
*
|
*
|
||||||
* Big thanks to RichardJ for contributing this Regular Expression
|
* Big thanks to RichardJ for contributing this Regular Expression
|
||||||
*/
|
*/
|
||||||
preg_match(
|
preg_match(
|
||||||
'/
|
'/
|
||||||
\A (
|
\A
|
||||||
|
# 1. Extract the template marker
|
||||||
|
(?:(\#\@\+|\#\@\-)\n?)?
|
||||||
|
|
||||||
|
# 2. Extract the summary
|
||||||
|
(?:
|
||||||
|
(?! @\pL ) # The summary may not start with an @
|
||||||
|
(
|
||||||
[^\n.]+
|
[^\n.]+
|
||||||
(?:
|
(?:
|
||||||
(?! \. \n | \n{2} ) # disallow the first seperator here
|
(?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines
|
||||||
[\n.] (?! [ \t]* @\pL ) # disallow second seperator
|
[\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line
|
||||||
[^\n.]+
|
[^\n.]+ # Include anything else
|
||||||
)*
|
)*
|
||||||
\.?
|
\.?
|
||||||
|
)?
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 3. Extract the description
|
||||||
(?:
|
(?:
|
||||||
\s* # first seperator (actually newlines but it\'s all whitespace)
|
\s* # Some form of whitespace _must_ precede a description because a summary must be there
|
||||||
(?! @\pL ) # disallow the rest, to make sure this one doesn\'t match,
|
(?! @\pL ) # The description may not start with an @
|
||||||
#if it doesn\'t exist
|
|
||||||
(
|
(
|
||||||
[^\n]+
|
[^\n]+
|
||||||
(?: \n+
|
(?: \n+
|
||||||
(?! [ \t]* @\pL ) # disallow second seperator (@param)
|
(?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line
|
||||||
[^\n]+
|
[^\n]+ # Include anything else
|
||||||
)*
|
)*
|
||||||
)
|
)
|
||||||
)?
|
)?
|
||||||
|
|
||||||
|
# 4. Extract the tags (anything that follows)
|
||||||
(\s+ [\s\S]*)? # everything that follows
|
(\s+ [\s\S]*)? # everything that follows
|
||||||
/ux',
|
/ux',
|
||||||
$comment,
|
$comment,
|
||||||
$matches
|
$matches
|
||||||
);
|
);
|
||||||
array_shift($matches);
|
array_shift($matches);
|
||||||
}
|
|
||||||
|
|
||||||
while (count($matches) < 3) {
|
while (count($matches) < 4) {
|
||||||
$matches[] = '';
|
$matches[] = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $matches;
|
return $matches;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +277,7 @@ class DocBlock implements \Reflector
|
|||||||
*/
|
*/
|
||||||
public function setText($comment)
|
public function setText($comment)
|
||||||
{
|
{
|
||||||
list($short, $long) = $this->splitDocBlock($comment);
|
list(,$short, $long) = $this->splitDocBlock($comment);
|
||||||
$this->short_description = $short;
|
$this->short_description = $short;
|
||||||
$this->long_description = new DocBlock\Description($long, $this);
|
$this->long_description = new DocBlock\Description($long, $this);
|
||||||
return $this;
|
return $this;
|
||||||
@@ -282,6 +302,44 @@ class DocBlock implements \Reflector
|
|||||||
return $this->long_description;
|
return $this->long_description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this DocBlock is the start of a Template section.
|
||||||
|
*
|
||||||
|
* A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker
|
||||||
|
* (`#@+`) that is appended directly after the opening `/**` of a DocBlock.
|
||||||
|
*
|
||||||
|
* An example of such an opening is:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* /**#@+
|
||||||
|
* * My DocBlock
|
||||||
|
* * /
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all
|
||||||
|
* elements that follow until another DocBlock is found that contains the closing marker (`#@-`).
|
||||||
|
*
|
||||||
|
* @see self::isTemplateEnd() for the check whether a closing marker was provided.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isTemplateStart()
|
||||||
|
{
|
||||||
|
return $this->isTemplateStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this DocBlock is the end of a Template section.
|
||||||
|
*
|
||||||
|
* @see self::isTemplateStart() for a more complete description of the Docblock Template functionality.
|
||||||
|
*
|
||||||
|
* @return boolean
|
||||||
|
*/
|
||||||
|
public function isTemplateEnd()
|
||||||
|
{
|
||||||
|
return $this->isTemplateEnd;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the current context.
|
* Returns the current context.
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -91,6 +91,41 @@ DOCBLOCK;
|
|||||||
$this->assertFalse($object->hasTag('category'));
|
$this->assertFalse($object->hasTag('category'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \phpDocumentor\Reflection\DocBlock::isTemplateStart
|
||||||
|
*/
|
||||||
|
public function testIfStartOfTemplateIsDiscovered()
|
||||||
|
{
|
||||||
|
$fixture = <<<DOCBLOCK
|
||||||
|
/**#@+
|
||||||
|
* @see \MyClass
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
DOCBLOCK;
|
||||||
|
$object = new DocBlock($fixture);
|
||||||
|
$this->assertEquals('', $object->getShortDescription());
|
||||||
|
$this->assertEquals('', $object->getLongDescription()->getContents());
|
||||||
|
$this->assertCount(2, $object->getTags());
|
||||||
|
$this->assertTrue($object->hasTag('see'));
|
||||||
|
$this->assertTrue($object->hasTag('return'));
|
||||||
|
$this->assertFalse($object->hasTag('category'));
|
||||||
|
$this->assertTrue($object->isTemplateStart());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @covers \phpDocumentor\Reflection\DocBlock::isTemplateEnd
|
||||||
|
*/
|
||||||
|
public function testIfEndOfTemplateIsDiscovered()
|
||||||
|
{
|
||||||
|
$fixture = <<<DOCBLOCK
|
||||||
|
/**#@-*/
|
||||||
|
DOCBLOCK;
|
||||||
|
$object = new DocBlock($fixture);
|
||||||
|
$this->assertEquals('', $object->getShortDescription());
|
||||||
|
$this->assertEquals('', $object->getLongDescription()->getContents());
|
||||||
|
$this->assertTrue($object->isTemplateEnd());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @covers \phpDocumentor\Reflection\DocBlock::cleanInput
|
* @covers \phpDocumentor\Reflection\DocBlock::cleanInput
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user