mirror of
https://github.com/barryvdh/ReflectionDocBlock.git
synced 2026-08-18 01:57:13 +00:00
This is especially important when using markdown. Adding a blank line between text introduces a new paragraph. Current implementation will result in all text staying in one paragraph and also other elements like lists and code do not get recognized in most cases.
411 lines
11 KiB
PHP
411 lines
11 KiB
PHP
<?php
|
|
/**
|
|
* phpDocumentor
|
|
*
|
|
* PHP Version 5.3
|
|
*
|
|
* @author Mike van Riel <[email protected]>
|
|
* @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
|
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
|
* @link http://phpdoc.org
|
|
*/
|
|
|
|
namespace phpDocumentor\Reflection;
|
|
|
|
use phpDocumentor\Reflection\DocBlock\Tag;
|
|
use phpDocumentor\Reflection\DocBlock\Context;
|
|
use phpDocumentor\Reflection\DocBlock\Location;
|
|
|
|
/**
|
|
* Parses the DocBlock for any structure.
|
|
*
|
|
* @author Mike van Riel <[email protected]>
|
|
* @license http://www.opensource.org/licenses/mit-license.php MIT
|
|
* @link http://phpdoc.org
|
|
*/
|
|
class DocBlock implements \Reflector
|
|
{
|
|
/** @var string The opening line for this docblock. */
|
|
protected $short_description = '';
|
|
|
|
/**
|
|
* @var DocBlock\Description The actual
|
|
* description for this docblock.
|
|
*/
|
|
protected $long_description = null;
|
|
|
|
/**
|
|
* @var Tag[] An array containing all
|
|
* the tags in this docblock; except inline.
|
|
*/
|
|
protected $tags = array();
|
|
|
|
/** @var Context Information about the context of this DocBlock. */
|
|
protected $context = null;
|
|
|
|
/** @var Location Information about the location of this DocBlock. */
|
|
protected $location = null;
|
|
|
|
/**
|
|
* Parses the given docblock and populates the member fields.
|
|
*
|
|
* The constructor may also receive namespace information such as the
|
|
* current namespace and aliases. This information is used by some tags
|
|
* (e.g. @return, @param, etc.) to turn a relative Type into a FQCN.
|
|
*
|
|
* @param \Reflector|string $docblock A docblock comment (including
|
|
* asterisks) or reflector supporting the getDocComment method.
|
|
* @param Context $context The context in which the DocBlock
|
|
* occurs.
|
|
* @param Location $location The location within the file that this
|
|
* DocBlock occurs in.
|
|
*
|
|
* @throws \InvalidArgumentException if the given argument does not have the
|
|
* getDocComment method.
|
|
*/
|
|
public function __construct(
|
|
$docblock,
|
|
Context $context = null,
|
|
Location $location = null
|
|
) {
|
|
if (is_object($docblock)) {
|
|
if (!method_exists($docblock, 'getDocComment')) {
|
|
throw new \InvalidArgumentException(
|
|
'Invalid object passed; the given reflector must support '
|
|
. 'the getDocComment method'
|
|
);
|
|
}
|
|
|
|
$docblock = $docblock->getDocComment();
|
|
}
|
|
|
|
$docblock = $this->cleanInput($docblock);
|
|
|
|
list($short, $long, $tags) = $this->splitDocBlock($docblock);
|
|
$this->short_description = $short;
|
|
$this->long_description = new DocBlock\Description($long, $this);
|
|
$this->parseTags($tags);
|
|
|
|
$this->context = $context;
|
|
$this->location = $location;
|
|
}
|
|
|
|
/**
|
|
* Strips the asterisks from the DocBlock comment.
|
|
*
|
|
* @param string $comment String containing the comment text.
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function cleanInput($comment)
|
|
{
|
|
$comment = trim(
|
|
preg_replace(
|
|
'#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u',
|
|
'$1',
|
|
$comment
|
|
)
|
|
);
|
|
|
|
// reg ex above is not able to remove */ from a single line docblock
|
|
if (substr($comment, -2) == '*/') {
|
|
$comment = trim(substr($comment, 0, -2));
|
|
}
|
|
|
|
// normalize strings
|
|
$comment = str_replace(array("\r\n", "\r"), "\n", $comment);
|
|
|
|
return $comment;
|
|
}
|
|
|
|
/**
|
|
* Splits the DocBlock into a short description, long description and
|
|
* block of tags.
|
|
*
|
|
* @param string $comment Comment to split into the sub-parts.
|
|
*
|
|
* @author RichardJ Special thanks to RichardJ for the regex responsible
|
|
* for the split.
|
|
*
|
|
* @return string[] containing the short-, long description and an element
|
|
* containing the tags.
|
|
*/
|
|
protected function splitDocBlock($comment)
|
|
{
|
|
if (strpos($comment, '@') === 0) {
|
|
$matches = array('', '', $comment);
|
|
} else {
|
|
// clears all extra horizontal whitespace from the line endings
|
|
// to prevent parsing issues
|
|
$comment = preg_replace('/\h*$/Sum', '', $comment);
|
|
|
|
/*
|
|
* Splits the docblock into a short description, long description and
|
|
* tags section
|
|
* - The short description is started from the first character until
|
|
* a dot is encountered followed by a newline OR
|
|
* two consecutive newlines (horizontal whitespace is taken into
|
|
* account to consider spacing errors)
|
|
* - The long description, any character until a new line is
|
|
* encountered followed by an @ and word characters (a tag).
|
|
* This is optional.
|
|
* - Tags; the remaining characters
|
|
*
|
|
* Big thanks to RichardJ for contributing this Regular Expression
|
|
*/
|
|
preg_match(
|
|
'/
|
|
\A (
|
|
[^\n.]+
|
|
(?:
|
|
(?! \. \n | \n{2} ) # disallow the first seperator here
|
|
[\n.] (?! [ \t]* @\pL ) # disallow second seperator
|
|
[^\n.]+
|
|
)*
|
|
\.?
|
|
)
|
|
(?:
|
|
\s* # first seperator (actually newlines but it\'s all whitespace)
|
|
(?! @\pL ) # disallow the rest, to make sure this one doesn\'t match,
|
|
#if it doesn\'t exist
|
|
(
|
|
[^\n]+
|
|
(?: \n+
|
|
(?! [ \t]* @\pL ) # disallow second seperator (@param)
|
|
[^\n]+
|
|
)*
|
|
)
|
|
)?
|
|
(\s+ [\s\S]*)? # everything that follows
|
|
/ux',
|
|
$comment,
|
|
$matches
|
|
);
|
|
array_shift($matches);
|
|
}
|
|
|
|
while (count($matches) < 3) {
|
|
$matches[] = '';
|
|
}
|
|
return $matches;
|
|
}
|
|
|
|
/**
|
|
* Creates the tag objects.
|
|
*
|
|
* @param string $tags Tag block to parse.
|
|
*
|
|
* @return void
|
|
*/
|
|
protected function parseTags($tags)
|
|
{
|
|
$result = array();
|
|
$tags = trim($tags);
|
|
if ('' !== $tags) {
|
|
if ('@' !== $tags[0]) {
|
|
throw new \LogicException(
|
|
'A tag block started with text instead of an actual tag,'
|
|
. ' this makes the tag block invalid: ' . $tags
|
|
);
|
|
}
|
|
foreach (explode("\n", $tags) as $tag_line) {
|
|
if (isset($tag_line[0]) && ($tag_line[0] === '@')) {
|
|
$result[] = $tag_line;
|
|
} else {
|
|
$result[count($result) - 1] .= "\n" . $tag_line;
|
|
}
|
|
}
|
|
|
|
// create proper Tag objects
|
|
foreach ($result as $key => $tag_line) {
|
|
$result[$key] = Tag::createInstance(trim($tag_line), $this);
|
|
}
|
|
}
|
|
|
|
$this->tags = $result;
|
|
}
|
|
|
|
/**
|
|
* Gets the text portion of the doc block.
|
|
*
|
|
* Gets the text portion (short and long description combined) of the doc
|
|
* block.
|
|
*
|
|
* @return string The text portion of the doc block.
|
|
*/
|
|
public function getText()
|
|
{
|
|
$short = $this->getShortDescription();
|
|
$long = $this->getLongDescription()->getContents();
|
|
|
|
if ($long) {
|
|
return "{$short}\n\n{$long}";
|
|
} else {
|
|
return $short;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set the text portion of the doc block.
|
|
*
|
|
* Sets the text portion (short and long description combined) of the doc
|
|
* block.
|
|
*
|
|
* @param string $docblock The new text portion of the doc block.
|
|
*
|
|
* @return $this This doc block.
|
|
*/
|
|
public function setText($comment)
|
|
{
|
|
list($short, $long) = $this->splitDocBlock($comment);
|
|
$this->short_description = $short;
|
|
$this->long_description = new DocBlock\Description($long, $this);
|
|
return $this;
|
|
}
|
|
/**
|
|
* Returns the opening line or also known as short description.
|
|
*
|
|
* @return string
|
|
*/
|
|
public function getShortDescription()
|
|
{
|
|
return $this->short_description;
|
|
}
|
|
|
|
/**
|
|
* Returns the full description or also known as long description.
|
|
*
|
|
* @return DocBlock\Description
|
|
*/
|
|
public function getLongDescription()
|
|
{
|
|
return $this->long_description;
|
|
}
|
|
|
|
/**
|
|
* Returns the current context.
|
|
*
|
|
* @return Context
|
|
*/
|
|
public function getContext()
|
|
{
|
|
return $this->context;
|
|
}
|
|
|
|
/**
|
|
* Returns the current location.
|
|
*
|
|
* @return Location
|
|
*/
|
|
public function getLocation()
|
|
{
|
|
return $this->location;
|
|
}
|
|
|
|
/**
|
|
* Returns the tags for this DocBlock.
|
|
*
|
|
* @return Tag[]
|
|
*/
|
|
public function getTags()
|
|
{
|
|
return $this->tags;
|
|
}
|
|
|
|
/**
|
|
* Returns an array of tags matching the given name. If no tags are found
|
|
* an empty array is returned.
|
|
*
|
|
* @param string $name String to search by.
|
|
*
|
|
* @return Tag[]
|
|
*/
|
|
public function getTagsByName($name)
|
|
{
|
|
$result = array();
|
|
|
|
/** @var Tag $tag */
|
|
foreach ($this->getTags() as $tag) {
|
|
if ($tag->getName() != $name) {
|
|
continue;
|
|
}
|
|
|
|
$result[] = $tag;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Checks if a tag of a certain type is present in this DocBlock.
|
|
*
|
|
* @param string $name Tag name to check for.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function hasTag($name)
|
|
{
|
|
/** @var Tag $tag */
|
|
foreach ($this->getTags() as $tag) {
|
|
if ($tag->getName() == $name) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Appends a tag at the end of the list of tags.
|
|
*
|
|
* @param Tag $tag The tag to add.
|
|
*
|
|
* @return Tag The newly added tag.
|
|
*
|
|
* @throws \LogicException When the tag belongs to a different DocBlock.
|
|
*/
|
|
public function appendTag(Tag $tag)
|
|
{
|
|
if (null === $tag->getDocBlock()) {
|
|
$tag->setDocBlock($this);
|
|
}
|
|
|
|
if ($tag->getDocBlock() === $this) {
|
|
$this->tags[] = $tag;
|
|
} else {
|
|
throw new \LogicException(
|
|
'This tag belongs to a different DocBlock object.'
|
|
);
|
|
}
|
|
|
|
return $tag;
|
|
}
|
|
|
|
|
|
/**
|
|
* Builds a string representation of this object.
|
|
*
|
|
* @todo determine the exact format as used by PHP Reflection and
|
|
* implement it.
|
|
*
|
|
* @return string
|
|
* @codeCoverageIgnore Not yet implemented
|
|
*/
|
|
public static function export()
|
|
{
|
|
throw new \Exception('Not yet implemented');
|
|
}
|
|
|
|
/**
|
|
* Returns the exported information (we should use the export static method
|
|
* BUT this throws an exception at this point).
|
|
*
|
|
* @return string
|
|
* @codeCoverageIgnore Not yet implemented
|
|
*/
|
|
public function __toString()
|
|
{
|
|
return 'Not yet implemented';
|
|
}
|
|
}
|