PageRenderTime 39ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 1ms

/Front_End/vendor/phpdocumentor/reflection-docblock/src/DocBlockFactory.php

https://gitlab.com/Sigpot/AirSpot
PHP | 277 lines | 155 code | 32 blank | 90 comment | 13 complexity | be536c5bca540267074698bc288852a3 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of phpDocumentor.
  4. *
  5. * For the full copyright and license information, please view the LICENSE
  6. * file that was distributed with this source code.
  7. *
  8. * @copyright 2010-2015 Mike van Riel<mike@phpdoc.org>
  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\DescriptionFactory;
  14. use phpDocumentor\Reflection\DocBlock\StandardTagFactory;
  15. use phpDocumentor\Reflection\DocBlock\Tag;
  16. use phpDocumentor\Reflection\DocBlock\TagFactory;
  17. use Webmozart\Assert\Assert;
  18. final class DocBlockFactory implements DocBlockFactoryInterface
  19. {
  20. /** @var DocBlock\DescriptionFactory */
  21. private $descriptionFactory;
  22. /** @var DocBlock\TagFactory */
  23. private $tagFactory;
  24. /**
  25. * Initializes this factory with the required subcontractors.
  26. *
  27. * @param DescriptionFactory $descriptionFactory
  28. * @param TagFactory $tagFactory
  29. */
  30. public function __construct(DescriptionFactory $descriptionFactory, TagFactory $tagFactory)
  31. {
  32. $this->descriptionFactory = $descriptionFactory;
  33. $this->tagFactory = $tagFactory;
  34. }
  35. /**
  36. * Factory method for easy instantiation.
  37. *
  38. * @param string[] $additionalTags
  39. *
  40. * @return DocBlockFactory
  41. */
  42. public static function createInstance(array $additionalTags = [])
  43. {
  44. $fqsenResolver = new FqsenResolver();
  45. $tagFactory = new StandardTagFactory($fqsenResolver);
  46. $descriptionFactory = new DescriptionFactory($tagFactory);
  47. $tagFactory->addService($descriptionFactory);
  48. $tagFactory->addService(new TypeResolver($fqsenResolver));
  49. $docBlockFactory = new self($descriptionFactory, $tagFactory);
  50. foreach ($additionalTags as $tagName => $tagHandler) {
  51. $docBlockFactory->registerTagHandler($tagName, $tagHandler);
  52. }
  53. return $docBlockFactory;
  54. }
  55. /**
  56. * @param object|string $docblock A string containing the DocBlock to parse or an object supporting the
  57. * getDocComment method (such as a ReflectionClass object).
  58. * @param Types\Context $context
  59. * @param Location $location
  60. *
  61. * @return DocBlock
  62. */
  63. public function create($docblock, Types\Context $context = null, Location $location = null)
  64. {
  65. if (is_object($docblock)) {
  66. if (!method_exists($docblock, 'getDocComment')) {
  67. $exceptionMessage = 'Invalid object passed; the given object must support the getDocComment method';
  68. throw new \InvalidArgumentException($exceptionMessage);
  69. }
  70. $docblock = $docblock->getDocComment();
  71. }
  72. Assert::stringNotEmpty($docblock);
  73. if ($context === null) {
  74. $context = new Types\Context('');
  75. }
  76. $parts = $this->splitDocBlock($this->stripDocComment($docblock));
  77. list($templateMarker, $summary, $description, $tags) = $parts;
  78. return new DocBlock(
  79. $summary,
  80. $description ? $this->descriptionFactory->create($description, $context) : null,
  81. array_filter($this->parseTagBlock($tags, $context), function($tag) {
  82. return $tag instanceof Tag;
  83. }),
  84. $context,
  85. $location,
  86. $templateMarker === '#@+',
  87. $templateMarker === '#@-'
  88. );
  89. }
  90. public function registerTagHandler($tagName, $handler)
  91. {
  92. $this->tagFactory->registerTagHandler($tagName, $handler);
  93. }
  94. /**
  95. * Strips the asterisks from the DocBlock comment.
  96. *
  97. * @param string $comment String containing the comment text.
  98. *
  99. * @return string
  100. */
  101. private function stripDocComment($comment)
  102. {
  103. $comment = trim(preg_replace('#[ \t]*(?:\/\*\*|\*\/|\*)?[ \t]{0,1}(.*)?#u', '$1', $comment));
  104. // reg ex above is not able to remove */ from a single line docblock
  105. if (substr($comment, -2) == '*/') {
  106. $comment = trim(substr($comment, 0, -2));
  107. }
  108. return str_replace(array("\r\n", "\r"), "\n", $comment);
  109. }
  110. /**
  111. * Splits the DocBlock into a template marker, summary, description and block of tags.
  112. *
  113. * @param string $comment Comment to split into the sub-parts.
  114. *
  115. * @author Richard van Velzen (@_richardJ) Special thanks to Richard for the regex responsible for the split.
  116. * @author Mike van Riel <me@mikevanriel.com> for extending the regex with template marker support.
  117. *
  118. * @return string[] containing the template marker (if any), summary, description and a string containing the tags.
  119. */
  120. private function splitDocBlock($comment)
  121. {
  122. // Performance improvement cheat: if the first character is an @ then only tags are in this DocBlock. This
  123. // method does not split tags so we return this verbatim as the fourth result (tags). This saves us the
  124. // performance impact of running a regular expression
  125. if (strpos($comment, '@') === 0) {
  126. return array('', '', '', $comment);
  127. }
  128. // clears all extra horizontal whitespace from the line endings to prevent parsing issues
  129. $comment = preg_replace('/\h*$/Sum', '', $comment);
  130. /*
  131. * Splits the docblock into a template marker, summary, description and tags section.
  132. *
  133. * - The template marker is empty, #@+ or #@- if the DocBlock starts with either of those (a newline may
  134. * occur after it and will be stripped).
  135. * - The short description is started from the first character until a dot is encountered followed by a
  136. * newline OR two consecutive newlines (horizontal whitespace is taken into account to consider spacing
  137. * errors). This is optional.
  138. * - The long description, any character until a new line is encountered followed by an @ and word
  139. * characters (a tag). This is optional.
  140. * - Tags; the remaining characters
  141. *
  142. * Big thanks to RichardJ for contributing this Regular Expression
  143. */
  144. preg_match(
  145. '/
  146. \A
  147. # 1. Extract the template marker
  148. (?:(\#\@\+|\#\@\-)\n?)?
  149. # 2. Extract the summary
  150. (?:
  151. (?! @\pL ) # The summary may not start with an @
  152. (
  153. [^\n.]+
  154. (?:
  155. (?! \. \n | \n{2} ) # End summary upon a dot followed by newline or two newlines
  156. [\n.] (?! [ \t]* @\pL ) # End summary when an @ is found as first character on a new line
  157. [^\n.]+ # Include anything else
  158. )*
  159. \.?
  160. )?
  161. )
  162. # 3. Extract the description
  163. (?:
  164. \s* # Some form of whitespace _must_ precede a description because a summary must be there
  165. (?! @\pL ) # The description may not start with an @
  166. (
  167. [^\n]+
  168. (?: \n+
  169. (?! [ \t]* @\pL ) # End description when an @ is found as first character on a new line
  170. [^\n]+ # Include anything else
  171. )*
  172. )
  173. )?
  174. # 4. Extract the tags (anything that follows)
  175. (\s+ [\s\S]*)? # everything that follows
  176. /ux',
  177. $comment,
  178. $matches
  179. );
  180. array_shift($matches);
  181. while (count($matches) < 4) {
  182. $matches[] = '';
  183. }
  184. return $matches;
  185. }
  186. /**
  187. * Creates the tag objects.
  188. *
  189. * @param string $tags Tag block to parse.
  190. * @param Types\Context $context Context of the parsed Tag
  191. *
  192. * @return DocBlock\Tag[]
  193. */
  194. private function parseTagBlock($tags, Types\Context $context)
  195. {
  196. $tags = $this->filterTagBlock($tags);
  197. if (!$tags) {
  198. return [];
  199. }
  200. $result = $this->splitTagBlockIntoTagLines($tags);
  201. foreach ($result as $key => $tagLine) {
  202. $result[$key] = $this->tagFactory->create(trim($tagLine), $context);
  203. }
  204. return $result;
  205. }
  206. /**
  207. * @param string $tags
  208. *
  209. * @return string[]
  210. */
  211. private function splitTagBlockIntoTagLines($tags)
  212. {
  213. $result = array();
  214. foreach (explode("\n", $tags) as $tag_line) {
  215. if (isset($tag_line[0]) && ($tag_line[0] === '@')) {
  216. $result[] = $tag_line;
  217. } else {
  218. $result[count($result) - 1] .= "\n" . $tag_line;
  219. }
  220. }
  221. return $result;
  222. }
  223. /**
  224. * @param $tags
  225. * @return string
  226. */
  227. private function filterTagBlock($tags)
  228. {
  229. $tags = trim($tags);
  230. if (!$tags) {
  231. return null;
  232. }
  233. if ('@' !== $tags[0]) {
  234. // @codeCoverageIgnoreStart
  235. // Can't simulate this; this only happens if there is an error with the parsing of the DocBlock that
  236. // we didn't foresee.
  237. throw new \LogicException('A tag block started with text instead of an at-sign(@): ' . $tags);
  238. // @codeCoverageIgnoreEnd
  239. }
  240. return $tags;
  241. }
  242. }