/library/Zend/Code/Scanner/DocBlockScanner.php

https://github.com/bruisedlee/zf2 · PHP · 375 lines · 221 code · 47 blank · 107 comment · 32 complexity · 8f30e53164c99b9f07b5194a527ca2fe MD5 · raw file

  1. <?php
  2. namespace Zend\Code\Scanner;
  3. use Zend\Code\Scanner,
  4. Zend\Code\NameInformation,
  5. Zend\Code\Annotation\AnnotationManager;
  6. class DocBlockScanner implements Scanner
  7. {
  8. /**
  9. * @var bool
  10. */
  11. protected $isScanned = false;
  12. /**
  13. * @var string
  14. */
  15. protected $docComment = null;
  16. /**
  17. * @var NameInformation
  18. */
  19. protected $nameInformation = null;
  20. /**
  21. * @var AnnotationManager
  22. */
  23. protected $annotationManager = null;
  24. /**
  25. * @var string
  26. */
  27. protected $shortDescription = null;
  28. /**
  29. * @var string
  30. */
  31. protected $longDescription = '';
  32. /**
  33. * @var array[]
  34. */
  35. protected $tags = array();
  36. /**
  37. * @var Annotation[]
  38. */
  39. protected $annotations = array();
  40. /**
  41. * @param string $docComment
  42. * @param AnnotationManager $annotationManager
  43. */
  44. public function __construct($docComment, NameInformation $nameInformation = null)
  45. {
  46. $this->docComment = $docComment;
  47. $this->nameInformation = $nameInformation;
  48. }
  49. /**
  50. * @return string
  51. */
  52. public function getShortDescription()
  53. {
  54. $this->scan();
  55. return $this->shortDescription;
  56. }
  57. /**
  58. * @return string
  59. */
  60. public function getLongDescription()
  61. {
  62. $this->scan();
  63. return $this->longDescription;
  64. }
  65. /**
  66. * @return array[]
  67. */
  68. public function getTags()
  69. {
  70. $this->scan();
  71. return $this->tags;
  72. }
  73. public function getAnnotations()
  74. {
  75. $this->scan();
  76. return $this->annotations;
  77. }
  78. protected function scan()
  79. {
  80. if ($this->isScanned) {
  81. return;
  82. }
  83. $tokens = $this->tokenize();
  84. $tagIndex = null;
  85. /*
  86. $currentAnnotationName = null;
  87. $currentAnnotationValue = '';
  88. */
  89. reset($tokens);
  90. SCANNER_TOP:
  91. $token = current($tokens);
  92. switch ($token[0]) {
  93. case 'DOCBLOCK_NEWLINE':
  94. if ($this->shortDescription === null) {
  95. $this->shortDescription = '';
  96. }
  97. goto SCANNER_CONTINUE;
  98. case 'DOCBKOCK_WHITESPACE':
  99. /*
  100. if ($currentAnnotationName === null) {
  101. goto SCANNER_CONTINUE;
  102. }
  103. */
  104. /*
  105. case 'DOCBLOCK_ANNOTATION_VALUE':
  106. if ($currentAnnotationName === null) {
  107. goto SCANNER_CONTINUE;
  108. }
  109. $currentAnnotationValue .= $token[1];
  110. goto SCANNER_CONTINUE;
  111. */
  112. case 'DOCBLOCK_TAG':
  113. array_push($this->tags, array('name' => $token[1], 'value' => ''));
  114. end($this->tags);
  115. $tagIndex = key($this->tags);
  116. /*
  117. if (!$this->annotationManager || !$this->annotationManager->hasAnnotationName(ltrim($token[1], '@'))) {
  118. goto SCANNER_CONTINUE;
  119. }
  120. */
  121. /*
  122. case 'DOCBLOCK_ANNOTATION_NAME':
  123. if ($currentAnnotationName !== null) {
  124. $this->annotations[] = $this->annotationManager->createAnnotation($currentAnnotationName, $currentAnnotationValue);
  125. $currentAnnotationName = $currentAnnotationValue = null;
  126. }
  127. $currentAnnotationName = ltrim($token[1], '@');
  128. if (!$this->annotationManager->hasAnnotationName($currentAnnotationName)) {
  129. $currentAnnotationName = null;
  130. }
  131. goto SCANNER_CONTINUE;
  132. */
  133. case 'DOCBLOCK_TEXT':
  134. if ($tagIndex !== null) {
  135. $this->tags[$tagIndex]['value'] .= ($this->tags[$tagIndex]['value'] == '') ? $token[1] : ' ' . $token[1];
  136. } elseif ($this->shortDescription !== null) {
  137. if ($this->shortDescription === '') {
  138. $this->shortDescription = $token[1];
  139. } else {
  140. $this->longDescription .= $token[1];
  141. }
  142. }
  143. goto SCANNER_CONTINUE;
  144. case 'DOCBLOCK_COMMENTEND':
  145. goto SCANNER_END;
  146. }
  147. SCANNER_CONTINUE:
  148. if (next($tokens) === false) {
  149. goto SCANNER_END;
  150. }
  151. goto SCANNER_TOP;
  152. SCANNER_END:
  153. /*
  154. if ($currentAnnotationName !== null) {
  155. $this->annotations[] = $this->annotationManager->createAnnotation($currentAnnotationName, $currentAnnotationValue);
  156. }
  157. */
  158. $this->shortDescription = rtrim($this->shortDescription);
  159. $this->longDescription = rtrim($this->longDescription);
  160. $this->isScanned = true;
  161. }
  162. protected function tokenize()
  163. {
  164. static $CONTEXT_INSIDE_DOCBLOCK = 0x01;
  165. static $CONTEXT_INSIDE_ASTERISK = 0x02;
  166. // static $CONTEXT_INSIDE_ANNOTATION = 0x04;
  167. $context = 0x00;
  168. $stream = $this->docComment;
  169. $streamIndex = null;
  170. $tokens = array();
  171. $tokenIndex = null;
  172. $currentChar = null;
  173. $currentWord = null;
  174. $currentLine = null;
  175. // $annotationMode = (isset($this->annotationManager));
  176. // $annotationParenCount = 0;
  177. $MACRO_STREAM_ADVANCE_CHAR = function ($positionsForward = 1) use (&$stream, &$streamIndex, &$currentChar, &$currentWord, &$currentLine, &$annotationMode) {
  178. $positionsForward = ($positionsForward > 0) ? $positionsForward : 1;
  179. $streamIndex = ($streamIndex === null) ? 0 : $streamIndex+$positionsForward;
  180. if (!isset($stream[$streamIndex])) {
  181. $currentChar = false;
  182. return false;
  183. }
  184. $currentChar = $stream[$streamIndex];
  185. $matches = array();
  186. $currentLine = (preg_match('#(.*)\n#', $stream, $matches, null, $streamIndex) === 1) ? $matches[1] : substr($stream, $streamIndex);
  187. if ($currentChar === ' ') {
  188. $currentWord = (preg_match('#( +)#', $currentLine, $matches) === 1) ? $matches[1] : $currentLine;
  189. } else {
  190. if ($annotationMode) {
  191. $currentWord = (($matches = strpos($currentLine, ' ')) !== false) ? substr($currentLine, 0, $matches) : $currentLine;
  192. } else {
  193. $currentWord = strtok($currentLine, " \n\t\r");
  194. }
  195. }
  196. return $currentChar;
  197. };
  198. $MACRO_STREAM_ADVANCE_WORD = function () use (&$currentWord, &$MACRO_STREAM_ADVANCE_CHAR) {
  199. return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentWord));
  200. };
  201. $MACRO_STREAM_ADVANCE_LINE = function () use (&$currentLine, &$MACRO_STREAM_ADVANCE_CHAR) {
  202. return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentLine));
  203. };
  204. $MACRO_TOKEN_ADVANCE = function () use (&$tokenIndex, &$tokens) {
  205. $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex+1;
  206. $tokens[$tokenIndex] = array('DOCBLOCK_UNKNOWN', '');
  207. };
  208. $MACRO_TOKEN_SET_TYPE = function ($type) use (&$tokenIndex, &$tokens) {
  209. $tokens[$tokenIndex][0] = $type;
  210. };
  211. $MACRO_TOKEN_APPEND_CHAR = function () use (&$currentChar, &$tokens, &$tokenIndex) {
  212. $tokens[$tokenIndex][1] .= $currentChar;
  213. };
  214. $MACRO_TOKEN_APPEND_WORD = function () use (&$currentWord, &$tokens, &$tokenIndex) {
  215. $tokens[$tokenIndex][1] .= $currentWord;
  216. };
  217. $MACRO_TOKEN_APPEND_WORD_PARTIAL = function ($length) use (&$currentWord, &$tokens, &$tokenIndex) {
  218. $tokens[$tokenIndex][1] .= substr($currentWord, 0, $length);
  219. };
  220. $MACRO_TOKEN_APPEND_LINE = function () use (&$currentLine, &$tokens, &$tokenIndex) {
  221. $tokens[$tokenIndex][1] .= $currentLine;
  222. };
  223. $MACRO_STREAM_ADVANCE_CHAR();
  224. $MACRO_TOKEN_ADVANCE();
  225. TOKENIZER_TOP:
  226. if ($context === 0x00 && $currentChar === '/' && $currentWord === '/**') {
  227. $MACRO_TOKEN_SET_TYPE('DOCBLOCK_COMMENTSTART');
  228. $MACRO_TOKEN_APPEND_WORD();
  229. $MACRO_TOKEN_ADVANCE();
  230. $context |= $CONTEXT_INSIDE_DOCBLOCK;
  231. $context |= $CONTEXT_INSIDE_ASTERISK;
  232. if ($MACRO_STREAM_ADVANCE_WORD() === false) {
  233. goto TOKENIZER_END;
  234. }
  235. goto TOKENIZER_TOP;
  236. }
  237. if ($context & $CONTEXT_INSIDE_DOCBLOCK && $currentWord === '*/') {
  238. $MACRO_TOKEN_SET_TYPE('DOCBLOCK_COMMENTEND');
  239. $MACRO_TOKEN_APPEND_WORD();
  240. $MACRO_TOKEN_ADVANCE();
  241. $context &= ~$CONTEXT_INSIDE_DOCBLOCK;
  242. if ($MACRO_STREAM_ADVANCE_WORD() === false) {
  243. goto TOKENIZER_END;
  244. }
  245. goto TOKENIZER_TOP;
  246. }
  247. if ($currentChar === ' ') {
  248. $MACRO_TOKEN_SET_TYPE(($context & $CONTEXT_INSIDE_ASTERISK) ? 'DOCBLOCK_WHITESPACE' : 'DOCBLOCK_WHITESPACE_INDENT');
  249. $MACRO_TOKEN_APPEND_WORD();
  250. $MACRO_TOKEN_ADVANCE();
  251. if ($MACRO_STREAM_ADVANCE_WORD() === false) {
  252. goto TOKENIZER_END;
  253. }
  254. goto TOKENIZER_TOP;
  255. }
  256. if ($currentChar === '*') {
  257. if (($context & $CONTEXT_INSIDE_DOCBLOCK) && ($context & $CONTEXT_INSIDE_ASTERISK)) {
  258. $MACRO_TOKEN_SET_TYPE('DOCBLOCK_TEXT');
  259. } else {
  260. $MACRO_TOKEN_SET_TYPE('DOCBLOCK_ASTERISK');
  261. $context |= $CONTEXT_INSIDE_ASTERISK;
  262. }
  263. $MACRO_TOKEN_APPEND_CHAR();
  264. $MACRO_TOKEN_ADVANCE();
  265. if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
  266. goto TOKENIZER_END;
  267. }
  268. goto TOKENIZER_TOP;
  269. }
  270. /*
  271. if ($currentChar === '@' && $annotationMode && ($startOfAnnotation = strpos($currentWord, '(')) !== false) {
  272. $MACRO_TOKEN_SET_TYPE('DOCBLOCK_ANNOTATION_NAME');
  273. $MACRO_TOKEN_APPEND_WORD_PARTIAL($startOfAnnotation);
  274. $MACRO_TOKEN_ADVANCE();
  275. $context |= $CONTEXT_INSIDE_ANNOTATION;
  276. if ($MACRO_STREAM_ADVANCE_CHAR($startOfAnnotation) === false) {
  277. goto TOKENIZER_END;
  278. }
  279. goto TOKENIZER_TOP;
  280. }
  281. */
  282. /*
  283. if ($annotationMode && ($context && $CONTEXT_INSIDE_ANNOTATION) && ($context && $CONTEXT_INSIDE_ASTERISK)) {
  284. $MACRO_TOKEN_SET_TYPE('DOCBLOCK_ANNOTATION_VALUE');
  285. $MACRO_TOKEN_APPEND_WORD();
  286. $MACRO_TOKEN_ADVANCE();
  287. $annotationParenCount += substr_count($currentWord, '(') - substr_count($currentWord, ')');
  288. if ($annotationParenCount === 0) {
  289. $context &= ~$CONTEXT_INSIDE_ANNOTATION;
  290. }
  291. if ($MACRO_STREAM_ADVANCE_WORD() === false) {
  292. goto TOKENIZER_END;
  293. }
  294. goto TOKENIZER_TOP;
  295. }
  296. */
  297. if ($currentChar === '@') {
  298. $MACRO_TOKEN_SET_TYPE('DOCBLOCK_TAG');
  299. $MACRO_TOKEN_APPEND_WORD();
  300. $MACRO_TOKEN_ADVANCE();
  301. if ($MACRO_STREAM_ADVANCE_WORD() === false) {
  302. goto TOKENIZER_END;
  303. }
  304. goto TOKENIZER_TOP;
  305. }
  306. if ($currentChar === "\n") {
  307. $MACRO_TOKEN_SET_TYPE('DOCBLOCK_NEWLINE');
  308. $MACRO_TOKEN_APPEND_CHAR();
  309. $MACRO_TOKEN_ADVANCE();
  310. $context &= ~$CONTEXT_INSIDE_ASTERISK;
  311. if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
  312. goto TOKENIZER_END;
  313. }
  314. goto TOKENIZER_TOP;
  315. }
  316. $MACRO_TOKEN_SET_TYPE('DOCBLOCK_TEXT');
  317. $MACRO_TOKEN_APPEND_LINE();
  318. $MACRO_TOKEN_ADVANCE();
  319. if ($MACRO_STREAM_ADVANCE_LINE() === false) {
  320. goto TOKENIZER_END;
  321. }
  322. goto TOKENIZER_TOP;
  323. TOKENIZER_END:
  324. array_pop($tokens);
  325. return $tokens;
  326. }
  327. }