PageRenderTime 24ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/vendor/phpdocumentor/reflection-docblock/src/phpDocumentor/Reflection/DocBlock.php

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