annotate vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock.php @ 2:92f882872392

Trusted hosts, + remove migration modules
author Chris Cannam
date Tue, 05 Dec 2017 09:26:43 +0000
parents 4c8ae668cc8c
children
rev   line source
Chris@0 1 <?php
Chris@0 2 /**
Chris@0 3 * phpDocumentor
Chris@0 4 *
Chris@0 5 * PHP Version 5.3
Chris@0 6 *
Chris@0 7 * @author Mike van Riel <mike.vanriel@naenius.com>
Chris@0 8 * @copyright 2010-2011 Mike van Riel / Naenius (http://www.naenius.com)
Chris@0 9 * @license http://www.opensource.org/licenses/mit-license.php MIT
Chris@0 10 * @link http://phpdoc.org
Chris@0 11 */
Chris@0 12
Chris@0 13 namespace phpDocumentor\Reflection;
Chris@0 14
Chris@0 15 use phpDocumentor\Reflection\DocBlock\Tag;
Chris@0 16 use phpDocumentor\Reflection\DocBlock\Context;
Chris@0 17 use phpDocumentor\Reflection\DocBlock\Location;
Chris@0 18
Chris@0 19 /**
Chris@0 20 * Parses the DocBlock for any structure.
Chris@0 21 *
Chris@0 22 * @author Mike van Riel <mike.vanriel@naenius.com>
Chris@0 23 * @license http://www.opensource.org/licenses/mit-license.php MIT
Chris@0 24 * @link http://phpdoc.org
Chris@0 25 */
Chris@0 26 class DocBlock implements \Reflector
Chris@0 27 {
Chris@0 28 /** @var string The opening line for this docblock. */
Chris@0 29 protected $short_description = '';
Chris@0 30
Chris@0 31 /**
Chris@0 32 * @var DocBlock\Description The actual
Chris@0 33 * description for this docblock.
Chris@0 34 */
Chris@0 35 protected $long_description = null;
Chris@0 36
Chris@0 37 /**
Chris@0 38 * @var Tag[] An array containing all
Chris@0 39 * the tags in this docblock; except inline.
Chris@0 40 */
Chris@0 41 protected $tags = array();
Chris@0 42
Chris@0 43 /** @var Context Information about the context of this DocBlock. */
Chris@0 44 protected $context = null;
Chris@0 45
Chris@0 46 /** @var Location Information about the location of this DocBlock. */
Chris@0 47 protected $location = null;
Chris@0 48
Chris@0 49 /** @var bool Is this DocBlock (the start of) a template? */
Chris@0 50 protected $isTemplateStart = false;
Chris@0 51
Chris@0 52 /** @var bool Does this DocBlock signify the end of a DocBlock template? */
Chris@0 53 protected $isTemplateEnd = false;
Chris@0 54
Chris@0 55 /**
Chris@0 56 * Parses the given docblock and populates the member fields.
Chris@0 57 *
Chris@0 58 * The constructor may also receive namespace information such as the
Chris@0 59 * current namespace and aliases. This information is used by some tags
Chris@0 60 * (e.g. @return, @param, etc.) to turn a relative Type into a FQCN.
Chris@0 61 *
Chris@0 62 * @param \Reflector|string $docblock A docblock comment (including
Chris@0 63 * asterisks) or reflector supporting the getDocComment method.
Chris@0 64 * @param Context $context The context in which the DocBlock
Chris@0 65 * occurs.
Chris@0 66 * @param Location $location The location within the file that this
Chris@0 67 * DocBlock occurs in.
Chris@0 68 *
Chris@0 69 * @throws \InvalidArgumentException if the given argument does not have the
Chris@0 70 * getDocComment method.
Chris@0 71 */
Chris@0 72 public function __construct(
Chris@0 73 $docblock,
Chris@0 74 Context $context = null,
Chris@0 75 Location $location = null
Chris@0 76 ) {
Chris@0 77 if (is_object($docblock)) {
Chris@0 78 if (!method_exists($docblock, 'getDocComment')) {
Chris@0 79 throw new \InvalidArgumentException(
Chris@0 80 'Invalid object passed; the given reflector must support '
Chris@0 81 . 'the getDocComment method'
Chris@0 82 );
Chris@0 83 }
Chris@0 84
Chris@0 85 $docblock = $docblock->getDocComment();
Chris@0 86 }
Chris@0 87
Chris@0 88 $docblock = $this->cleanInput($docblock);
Chris@0 89
Chris@0 90 list($templateMarker, $short, $long, $tags) = $this->splitDocBlock($docblock);
Chris@0 91 $this->isTemplateStart = $templateMarker === '#@+';
Chris@0 92 $this->isTemplateEnd = $templateMarker === '#@-';
Chris@0 93 $this->short_description = $short;
Chris@0 94 $this->long_description = new DocBlock\Description($long, $this);
Chris@0 95 $this->parseTags($tags);
Chris@0 96
Chris@0 97 $this->context = $context;
Chris@0 98 $this->location = $location;
Chris@0 99 }
Chris@0 100
Chris@0 101 /**
Chris@0 102 * Strips the asterisks from the DocBlock comment.
Chris@0 103 *
Chris@0 104 * @param string $comment String containing the comment text.
Chris@0 105 *
Chris@0 106 * @return string
Chris@0 107 */
Chris@0 108 protected function cleanInput($comment)
Chris@0 109 {
Chris@0 110 $comment = trim(
Chris@0 111 preg_replace(
Chris@0 112 '#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u',
Chris@0 113 '$1',
Chris@0 114 $comment
Chris@0 115 )
Chris@0 116 );
Chris@0 117
Chris@0 118 // reg ex above is not able to remove */ from a single line docblock
Chris@0 119 if (substr($comment, -2) == '*/') {
Chris@0 120 $comment = trim(substr($comment, 0, -2));
Chris@0 121 }
Chris@0 122
Chris@0 123 // normalize strings
Chris@0 124 $comment = str_replace(array("\r\n", "\r"), "\n", $comment);
Chris@0 125
Chris@0 126 return $comment;
Chris@0 127 }
Chris@0 128
Chris@0 129 /**
Chris@0 130 * Splits the DocBlock into a template marker, summary, description and block of tags.
Chris@0 131 *
Chris@0 132 * @param string $comment Comment to split into the sub-parts.
Chris@0 133 *
Chris@0 134 * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
Chris@0 135 * @author Mike van Riel <me@mikevanriel.com> for extending the regex with template marker support.
Chris@0 136 *
Chris@0 137 * @return string[] containing the template marker (if any), summary, description and a string containing the tags.
Chris@0 138 */
Chris@0 139 protected function splitDocBlock($comment)
Chris@0 140 {
Chris@0 141 // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This
Chris@0 142 // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the
Chris@0 143 // performance impact of running a regular expression
Chris@0 144 if (strpos($comment, '@') === 0) {
Chris@0 145 return array('', '', '', $comment);
Chris@0 146 }
Chris@0 147
Chris@0 148 // clears all extra horizontal whitespace from the line endings to prevent parsing issues
Chris@0 149 $comment = preg_replace('/\h*$/Sum', '', $comment);
Chris@0 150
Chris@0 151 /*
Chris@0 152 * Splits the docblock into a template marker, short description, long description and tags section
Chris@0 153 *
Chris@0 154 * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may
Chris@0 155 * occur after it and will be stripped).
Chris@0 156 * - The short description is started from the first character until a dot is encountered followed by a
Chris@0 157 * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing
Chris@0 158 * errors). This is optional.
Chris@0 159 * - The long description, any character until a new line is encountered followed by an @ and word
Chris@0 160 * characters (a tag). This is optional.
Chris@0 161 * - Tags; the remaining characters
Chris@0 162 *
Chris@0 163 * Big thanks to RichardJ for contributing this Regular Expression
Chris@0 164 */
Chris@0 165 preg_match(
Chris@0 166 '/
Chris@0 167 \A
Chris@0 168 # 1. Extract the template marker
Chris@0 169 (?:(\#\@\+|\#\@\-)\n?)?
Chris@0 170
Chris@0 171 # 2. Extract the summary
Chris@0 172 (?:
Chris@0 173 (?! @\pL ) # The summary may not start with an @
Chris@0 174 (
Chris@0 175 [^\n.]+
Chris@0 176 (?:
Chris@0 177 (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines
Chris@0 178 [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line
Chris@0 179 [^\n.]+ # Include anything else
Chris@0 180 )*
Chris@0 181 \.?
Chris@0 182 )?
Chris@0 183 )
Chris@0 184
Chris@0 185 # 3. Extract the description
Chris@0 186 (?:
Chris@0 187 \s* # Some form of whitespace _must_ precede a description because a summary must be there
Chris@0 188 (?! @\pL ) # The description may not start with an @
Chris@0 189 (
Chris@0 190 [^\n]+
Chris@0 191 (?: \n+
Chris@0 192 (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line
Chris@0 193 [^\n]+ # Include anything else
Chris@0 194 )*
Chris@0 195 )
Chris@0 196 )?
Chris@0 197
Chris@0 198 # 4. Extract the tags (anything that follows)
Chris@0 199 (\s+ [\s\S]*)? # everything that follows
Chris@0 200 /ux',
Chris@0 201 $comment,
Chris@0 202 $matches
Chris@0 203 );
Chris@0 204 array_shift($matches);
Chris@0 205
Chris@0 206 while (count($matches) < 4) {
Chris@0 207 $matches[] = '';
Chris@0 208 }
Chris@0 209
Chris@0 210 return $matches;
Chris@0 211 }
Chris@0 212
Chris@0 213 /**
Chris@0 214 * Creates the tag objects.
Chris@0 215 *
Chris@0 216 * @param string $tags Tag block to parse.
Chris@0 217 *
Chris@0 218 * @return void
Chris@0 219 */
Chris@0 220 protected function parseTags($tags)
Chris@0 221 {
Chris@0 222 $result = array();
Chris@0 223 $tags = trim($tags);
Chris@0 224 if ('' !== $tags) {
Chris@0 225 if ('@' !== $tags[0]) {
Chris@0 226 throw new \LogicException(
Chris@0 227 'A tag block started with text instead of an actual tag,'
Chris@0 228 . ' this makes the tag block invalid: ' . $tags
Chris@0 229 );
Chris@0 230 }
Chris@0 231 foreach (explode("\n", $tags) as $tag_line) {
Chris@0 232 if (isset($tag_line[0]) && ($tag_line[0] === '@')) {
Chris@0 233 $result[] = $tag_line;
Chris@0 234 } else {
Chris@0 235 $result[count($result) - 1] .= "\n" . $tag_line;
Chris@0 236 }
Chris@0 237 }
Chris@0 238
Chris@0 239 // create proper Tag objects
Chris@0 240 foreach ($result as $key => $tag_line) {
Chris@0 241 $result[$key] = Tag::createInstance(trim($tag_line), $this);
Chris@0 242 }
Chris@0 243 }
Chris@0 244
Chris@0 245 $this->tags = $result;
Chris@0 246 }
Chris@0 247
Chris@0 248 /**
Chris@0 249 * Gets the text portion of the doc block.
Chris@0 250 *
Chris@0 251 * Gets the text portion (short and long description combined) of the doc
Chris@0 252 * block.
Chris@0 253 *
Chris@0 254 * @return string The text portion of the doc block.
Chris@0 255 */
Chris@0 256 public function getText()
Chris@0 257 {
Chris@0 258 $short = $this->getShortDescription();
Chris@0 259 $long = $this->getLongDescription()->getContents();
Chris@0 260
Chris@0 261 if ($long) {
Chris@0 262 return "{$short}\n\n{$long}";
Chris@0 263 } else {
Chris@0 264 return $short;
Chris@0 265 }
Chris@0 266 }
Chris@0 267
Chris@0 268 /**
Chris@0 269 * Set the text portion of the doc block.
Chris@0 270 *
Chris@0 271 * Sets the text portion (short and long description combined) of the doc
Chris@0 272 * block.
Chris@0 273 *
Chris@0 274 * @param string $docblock The new text portion of the doc block.
Chris@0 275 *
Chris@0 276 * @return $this This doc block.
Chris@0 277 */
Chris@0 278 public function setText($comment)
Chris@0 279 {
Chris@0 280 list(,$short, $long) = $this->splitDocBlock($comment);
Chris@0 281 $this->short_description = $short;
Chris@0 282 $this->long_description = new DocBlock\Description($long, $this);
Chris@0 283 return $this;
Chris@0 284 }
Chris@0 285 /**
Chris@0 286 * Returns the opening line or also known as short description.
Chris@0 287 *
Chris@0 288 * @return string
Chris@0 289 */
Chris@0 290 public function getShortDescription()
Chris@0 291 {
Chris@0 292 return $this->short_description;
Chris@0 293 }
Chris@0 294
Chris@0 295 /**
Chris@0 296 * Returns the full description or also known as long description.
Chris@0 297 *
Chris@0 298 * @return DocBlock\Description
Chris@0 299 */
Chris@0 300 public function getLongDescription()
Chris@0 301 {
Chris@0 302 return $this->long_description;
Chris@0 303 }
Chris@0 304
Chris@0 305 /**
Chris@0 306 * Returns whether this DocBlock is the start of a Template section.
Chris@0 307 *
Chris@0 308 * A Docblock may serve as template for a series of subsequent DocBlocks. This is indicated by a special marker
Chris@0 309 * (`#@+`) that is appended directly after the opening `/**` of a DocBlock.
Chris@0 310 *
Chris@0 311 * An example of such an opening is:
Chris@0 312 *
Chris@0 313 * ```
Chris@0 314 * /**#@+
Chris@0 315 * * My DocBlock
Chris@0 316 * * /
Chris@0 317 * ```
Chris@0 318 *
Chris@0 319 * The description and tags (not the summary!) are copied onto all subsequent DocBlocks and also applied to all
Chris@0 320 * elements that follow until another DocBlock is found that contains the closing marker (`#@-`).
Chris@0 321 *
Chris@0 322 * @see self::isTemplateEnd() for the check whether a closing marker was provided.
Chris@0 323 *
Chris@0 324 * @return boolean
Chris@0 325 */
Chris@0 326 public function isTemplateStart()
Chris@0 327 {
Chris@0 328 return $this->isTemplateStart;
Chris@0 329 }
Chris@0 330
Chris@0 331 /**
Chris@0 332 * Returns whether this DocBlock is the end of a Template section.
Chris@0 333 *
Chris@0 334 * @see self::isTemplateStart() for a more complete description of the Docblock Template functionality.
Chris@0 335 *
Chris@0 336 * @return boolean
Chris@0 337 */
Chris@0 338 public function isTemplateEnd()
Chris@0 339 {
Chris@0 340 return $this->isTemplateEnd;
Chris@0 341 }
Chris@0 342
Chris@0 343 /**
Chris@0 344 * Returns the current context.
Chris@0 345 *
Chris@0 346 * @return Context
Chris@0 347 */
Chris@0 348 public function getContext()
Chris@0 349 {
Chris@0 350 return $this->context;
Chris@0 351 }
Chris@0 352
Chris@0 353 /**
Chris@0 354 * Returns the current location.
Chris@0 355 *
Chris@0 356 * @return Location
Chris@0 357 */
Chris@0 358 public function getLocation()
Chris@0 359 {
Chris@0 360 return $this->location;
Chris@0 361 }
Chris@0 362
Chris@0 363 /**
Chris@0 364 * Returns the tags for this DocBlock.
Chris@0 365 *
Chris@0 366 * @return Tag[]
Chris@0 367 */
Chris@0 368 public function getTags()
Chris@0 369 {
Chris@0 370 return $this->tags;
Chris@0 371 }
Chris@0 372
Chris@0 373 /**
Chris@0 374 * Returns an array of tags matching the given name. If no tags are found
Chris@0 375 * an empty array is returned.
Chris@0 376 *
Chris@0 377 * @param string $name String to search by.
Chris@0 378 *
Chris@0 379 * @return Tag[]
Chris@0 380 */
Chris@0 381 public function getTagsByName($name)
Chris@0 382 {
Chris@0 383 $result = array();
Chris@0 384
Chris@0 385 /** @var Tag $tag */
Chris@0 386 foreach ($this->getTags() as $tag) {
Chris@0 387 if ($tag->getName() != $name) {
Chris@0 388 continue;
Chris@0 389 }
Chris@0 390
Chris@0 391 $result[] = $tag;
Chris@0 392 }
Chris@0 393
Chris@0 394 return $result;
Chris@0 395 }
Chris@0 396
Chris@0 397 /**
Chris@0 398 * Checks if a tag of a certain type is present in this DocBlock.
Chris@0 399 *
Chris@0 400 * @param string $name Tag name to check for.
Chris@0 401 *
Chris@0 402 * @return bool
Chris@0 403 */
Chris@0 404 public function hasTag($name)
Chris@0 405 {
Chris@0 406 /** @var Tag $tag */
Chris@0 407 foreach ($this->getTags() as $tag) {
Chris@0 408 if ($tag->getName() == $name) {
Chris@0 409 return true;
Chris@0 410 }
Chris@0 411 }
Chris@0 412
Chris@0 413 return false;
Chris@0 414 }
Chris@0 415
Chris@0 416 /**
Chris@0 417 * Appends a tag at the end of the list of tags.
Chris@0 418 *
Chris@0 419 * @param Tag $tag The tag to add.
Chris@0 420 *
Chris@0 421 * @return Tag The newly added tag.
Chris@0 422 *
Chris@0 423 * @throws \LogicException When the tag belongs to a different DocBlock.
Chris@0 424 */
Chris@0 425 public function appendTag(Tag $tag)
Chris@0 426 {
Chris@0 427 if (null === $tag->getDocBlock()) {
Chris@0 428 $tag->setDocBlock($this);
Chris@0 429 }
Chris@0 430
Chris@0 431 if ($tag->getDocBlock() === $this) {
Chris@0 432 $this->tags[] = $tag;
Chris@0 433 } else {
Chris@0 434 throw new \LogicException(
Chris@0 435 'This tag belongs to a different DocBlock object.'
Chris@0 436 );
Chris@0 437 }
Chris@0 438
Chris@0 439 return $tag;
Chris@0 440 }
Chris@0 441
Chris@0 442
Chris@0 443 /**
Chris@0 444 * Builds a string representation of this object.
Chris@0 445 *
Chris@0 446 * @todo determine the exact format as used by PHP Reflection and
Chris@0 447 * implement it.
Chris@0 448 *
Chris@0 449 * @return string
Chris@0 450 * @codeCoverageIgnore Not yet implemented
Chris@0 451 */
Chris@0 452 public static function export()
Chris@0 453 {
Chris@0 454 throw new \Exception('Not yet implemented');
Chris@0 455 }
Chris@0 456
Chris@0 457 /**
Chris@0 458 * Returns the exported information (we should use the export static method
Chris@0 459 * BUT this throws an exception at this point).
Chris@0 460 *
Chris@0 461 * @return string
Chris@0 462 * @codeCoverageIgnore Not yet implemented
Chris@0 463 */
Chris@0 464 public function __toString()
Chris@0 465 {
Chris@0 466 return 'Not yet implemented';
Chris@0 467 }
Chris@0 468 }