/libraries/Zend/Code/Scanner/AnnotationScanner.php

https://github.com/kiranatama/sagalaya · PHP · 331 lines · 252 code · 49 blank · 30 comment · 44 complexity · c18bdcfd711acb67eb8381891df20b23 MD5 · raw file

  1. <?php
  2. /**
  3. * Zend Framework (http://framework.zend.com/)
  4. *
  5. * @link http://github.com/zendframework/zf2 for the canonical source repository
  6. * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
  7. * @license http://framework.zend.com/license/new-bsd New BSD License
  8. * @package Zend_Code
  9. */
  10. namespace Zend\Code\Scanner;
  11. use Zend\Code\Annotation\AnnotationCollection;
  12. use Zend\Code\Annotation\AnnotationManager;
  13. use Zend\Code\NameInformation;
  14. class AnnotationScanner extends AnnotationCollection implements ScannerInterface
  15. {
  16. /**
  17. * @var bool
  18. */
  19. protected $isScanned = false;
  20. /**
  21. * @var string
  22. */
  23. protected $docComment = null;
  24. /**
  25. * @var NameInformation
  26. */
  27. protected $nameInformation = null;
  28. /**
  29. * @var AnnotationManager
  30. */
  31. protected $annotationManager = null;
  32. /**
  33. * @var AnnotationCollection[]
  34. */
  35. protected $annotations = array();
  36. /**
  37. * @param AnnotationManager $annotationManager
  38. * @param string $docComment
  39. * @param NameInformation $nameInformation
  40. * @return AnnotationScanner
  41. *
  42. */
  43. public function __construct(AnnotationManager $annotationManager, $docComment,
  44. NameInformation $nameInformation = null)
  45. {
  46. $this->annotationManager = $annotationManager;
  47. $this->docComment = $docComment;
  48. $this->nameInformation = $nameInformation;
  49. $this->scan($this->tokenize());
  50. }
  51. public function setNameInformation(NameInformation $nameInformation)
  52. {
  53. $this->nameInformation = $nameInformation;
  54. }
  55. protected function scan(array $tokens)
  56. {
  57. $annotations = array();
  58. $annotationIndex = -1;
  59. $contentEnd = false;
  60. reset($tokens);
  61. SCANNER_TOP:
  62. $token = current($tokens);
  63. switch ($token[0]) {
  64. case 'ANNOTATION_CLASS':
  65. $contentEnd = false;
  66. $annotationIndex++;
  67. $class = substr($token[1], 1);
  68. $class = $this->nameInformation->resolveName($class);
  69. $annotations[$annotationIndex] = array($class, null);
  70. goto SCANNER_CONTINUE;
  71. case 'ANNOTATION_CONTENT_START':
  72. $annotations[$annotationIndex][1] = '';
  73. case 'ANNOTATION_CONTENT_END':
  74. case 'ANNOTATION_CONTENT':
  75. case 'ANNOTATION_WHITESPACE':
  76. case 'ANNOTATION_NEWLINE':
  77. if (!$contentEnd && isset($annotations[$annotationIndex]) && is_string($annotations[$annotationIndex][1])) {
  78. $annotations[$annotationIndex][1] .= $token[1];
  79. }
  80. if ($token[0] === 'ANNOTATION_CONTENT_END') {
  81. $contentEnd = true;
  82. }
  83. goto SCANNER_CONTINUE;
  84. }
  85. SCANNER_CONTINUE:
  86. if (next($tokens) === false) {
  87. goto SCANNER_END;
  88. }
  89. goto SCANNER_TOP;
  90. SCANNER_END:
  91. foreach ($annotations as $annotation) {
  92. $annotation[] = '@' . $annotation[0] . $annotation[1];
  93. $annotationObject = $this->annotationManager->createAnnotation($annotation);
  94. if ($annotationObject) {
  95. $this->append($annotationObject);
  96. }
  97. }
  98. }
  99. protected function tokenize()
  100. {
  101. static $CONTEXT_DOCBLOCK = 0x01;
  102. static $CONTEXT_ASTERISK = 0x02;
  103. static $CONTEXT_CLASS = 0x04;
  104. static $CONTEXT_CONTENT = 0x08;
  105. $context = 0x00;
  106. $stream = $this->docComment;
  107. $streamIndex = null;
  108. $tokens = array();
  109. $tokenIndex = null;
  110. $currentChar = null;
  111. $currentWord = null;
  112. $currentLine = null;
  113. $annotationParentCount = 0;
  114. $MACRO_STREAM_ADVANCE_CHAR = function ($positionsForward = 1) use (&$stream, &$streamIndex, &$currentChar, &$currentWord, &$currentLine, &$annotationMode) {
  115. $positionsForward = ($positionsForward > 0) ? $positionsForward : 1;
  116. $streamIndex = ($streamIndex === null) ? 0 : $streamIndex + $positionsForward;
  117. if (!isset($stream[$streamIndex])) {
  118. $currentChar = false;
  119. return false;
  120. }
  121. $currentChar = $stream[$streamIndex];
  122. $matches = array();
  123. $currentLine = (preg_match('#(.*)\n#', $stream, $matches, null,
  124. $streamIndex) === 1) ? $matches[1] : substr($stream, $streamIndex);
  125. if ($currentChar === ' ') {
  126. $currentWord = (preg_match('#( +)#', $currentLine, $matches) === 1) ? $matches[1] : $currentLine;
  127. } else {
  128. $currentWord = (($matches = strpos($currentLine, ' ')) !== false) ? substr($currentLine, 0,
  129. $matches) : $currentLine;
  130. }
  131. return $currentChar;
  132. };
  133. $MACRO_STREAM_ADVANCE_WORD = function () use (&$currentWord, &$MACRO_STREAM_ADVANCE_CHAR) {
  134. return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentWord));
  135. };
  136. $MACRO_STREAM_ADVANCE_LINE = function () use (&$currentLine, &$MACRO_STREAM_ADVANCE_CHAR) {
  137. return $MACRO_STREAM_ADVANCE_CHAR(strlen($currentLine));
  138. };
  139. $MACRO_TOKEN_ADVANCE = function () use (&$tokenIndex, &$tokens) {
  140. $tokenIndex = ($tokenIndex === null) ? 0 : $tokenIndex + 1;
  141. $tokens[$tokenIndex] = array('ANNOTATION_UNKNOWN', '');
  142. };
  143. $MACRO_TOKEN_SET_TYPE = function ($type) use (&$tokenIndex, &$tokens) {
  144. $tokens[$tokenIndex][0] = $type;
  145. };
  146. $MACRO_TOKEN_APPEND_CHAR = function () use (&$currentChar, &$tokens, &$tokenIndex) {
  147. $tokens[$tokenIndex][1] .= $currentChar;
  148. };
  149. $MACRO_TOKEN_APPEND_WORD = function () use (&$currentWord, &$tokens, &$tokenIndex) {
  150. $tokens[$tokenIndex][1] .= $currentWord;
  151. };
  152. $MACRO_TOKEN_APPEND_LINE = function () use (&$currentLine, &$tokens, &$tokenIndex) {
  153. $tokens[$tokenIndex][1] .= $currentLine;
  154. };
  155. $MACRO_HAS_CONTEXT = function ($which) use (&$context) {
  156. return (($context & $which) === $which);
  157. };
  158. $MACRO_STREAM_ADVANCE_CHAR();
  159. $MACRO_TOKEN_ADVANCE();
  160. TOKENIZER_TOP:
  161. if ($context === 0x00 && $currentChar === '/' && $currentWord === '/**') {
  162. $MACRO_TOKEN_SET_TYPE('ANNOTATION_COMMENTSTART');
  163. $MACRO_TOKEN_APPEND_WORD();
  164. $MACRO_TOKEN_ADVANCE();
  165. $context |= $CONTEXT_DOCBLOCK;
  166. $context |= $CONTEXT_ASTERISK;
  167. if ($MACRO_STREAM_ADVANCE_WORD() === false) {
  168. goto TOKENIZER_END;
  169. }
  170. goto TOKENIZER_TOP;
  171. }
  172. if ($MACRO_HAS_CONTEXT($CONTEXT_CLASS)) {
  173. if (in_array($currentChar, array(' ', '(', "\n"))) {
  174. $context &= ~$CONTEXT_CLASS;
  175. $MACRO_TOKEN_ADVANCE();
  176. } else {
  177. $MACRO_TOKEN_APPEND_CHAR();
  178. if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
  179. goto TOKENIZER_END;
  180. }
  181. goto TOKENIZER_TOP;
  182. }
  183. }
  184. if ($currentChar === "\n") {
  185. $MACRO_TOKEN_SET_TYPE('ANNOTATION_NEWLINE');
  186. $MACRO_TOKEN_APPEND_CHAR();
  187. $MACRO_TOKEN_ADVANCE();
  188. $context &= ~$CONTEXT_ASTERISK;
  189. $context &= ~$CONTEXT_CLASS;
  190. if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
  191. goto TOKENIZER_END;
  192. }
  193. goto TOKENIZER_TOP;
  194. }
  195. if ($currentChar === ' ') {
  196. $MACRO_TOKEN_SET_TYPE(($MACRO_HAS_CONTEXT($CONTEXT_ASTERISK)) ? 'ANNOTATION_WHITESPACE' : 'ANNOTATION_WHITESPACE_INDENT');
  197. $MACRO_TOKEN_APPEND_WORD();
  198. $MACRO_TOKEN_ADVANCE();
  199. if ($MACRO_STREAM_ADVANCE_WORD() === false) {
  200. goto TOKENIZER_END;
  201. }
  202. goto TOKENIZER_TOP;
  203. }
  204. if ($MACRO_HAS_CONTEXT($CONTEXT_CONTENT) && $MACRO_HAS_CONTEXT($CONTEXT_ASTERISK)) {
  205. $MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT');
  206. $annotationParentCount += substr_count($currentWord, '(');
  207. $annotationParentCount -= substr_count($currentWord, ')');
  208. if ($annotationParentCount === 0) {
  209. $context &= ~$CONTEXT_CONTENT;
  210. $MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT_END');
  211. }
  212. $MACRO_TOKEN_APPEND_WORD();
  213. $MACRO_TOKEN_ADVANCE();
  214. if ($MACRO_STREAM_ADVANCE_WORD() === false) {
  215. goto TOKENIZER_END;
  216. }
  217. goto TOKENIZER_TOP;
  218. }
  219. if ($currentChar === '(' && $tokens[$tokenIndex - 1][0] === 'ANNOTATION_CLASS') {
  220. $context |= $CONTEXT_CONTENT;
  221. $annotationParentCount = 1;
  222. $MACRO_TOKEN_SET_TYPE('ANNOTATION_CONTENT_START');
  223. $MACRO_TOKEN_APPEND_CHAR();
  224. $MACRO_TOKEN_ADVANCE();
  225. if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
  226. goto TOKENIZER_END;
  227. }
  228. goto TOKENIZER_TOP;
  229. }
  230. if ($MACRO_HAS_CONTEXT($CONTEXT_DOCBLOCK) && $currentWord === '*/') {
  231. $MACRO_TOKEN_SET_TYPE('ANNOTATION_COMMENTEND');
  232. $MACRO_TOKEN_APPEND_WORD();
  233. $MACRO_TOKEN_ADVANCE();
  234. $context &= ~$CONTEXT_DOCBLOCK;
  235. if ($MACRO_STREAM_ADVANCE_WORD() === false) {
  236. goto TOKENIZER_END;
  237. }
  238. goto TOKENIZER_TOP;
  239. }
  240. if ($currentChar === '*') {
  241. if ($MACRO_HAS_CONTEXT($CONTEXT_DOCBLOCK) && ($MACRO_HAS_CONTEXT($CONTEXT_ASTERISK))) {
  242. $MACRO_TOKEN_SET_TYPE('ANNOTATION_IGNORE');
  243. } else {
  244. $MACRO_TOKEN_SET_TYPE('ANNOTATION_ASTERISK');
  245. $context |= $CONTEXT_ASTERISK;
  246. }
  247. $MACRO_TOKEN_APPEND_CHAR();
  248. $MACRO_TOKEN_ADVANCE();
  249. if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
  250. goto TOKENIZER_END;
  251. }
  252. goto TOKENIZER_TOP;
  253. }
  254. if ($currentChar === '@') {
  255. $MACRO_TOKEN_SET_TYPE('ANNOTATION_CLASS');
  256. $context |= $CONTEXT_CLASS;
  257. $MACRO_TOKEN_APPEND_CHAR();
  258. if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
  259. goto TOKENIZER_END;
  260. }
  261. goto TOKENIZER_TOP;
  262. }
  263. TOKENIZER_CONTINUE:
  264. if ($context && $CONTEXT_CONTENT) {
  265. $MACRO_TOKEN_APPEND_CHAR();
  266. if ($MACRO_STREAM_ADVANCE_CHAR() === false) {
  267. goto TOKENIZER_END;
  268. }
  269. } else {
  270. $MACRO_TOKEN_SET_TYPE('ANNOTATION_IGNORE');
  271. $MACRO_TOKEN_APPEND_LINE();
  272. $MACRO_TOKEN_ADVANCE();
  273. if ($MACRO_STREAM_ADVANCE_LINE() === false) {
  274. goto TOKENIZER_END;
  275. }
  276. }
  277. goto TOKENIZER_TOP;
  278. TOKENIZER_END:
  279. array_pop($tokens);
  280. return $tokens;
  281. }
  282. }