PageRenderTime 42ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/www/libs/nette-dev/Reflection/AnnotationsParser.php

https://github.com/bazo/Mokuji
PHP | 301 lines | 229 code | 37 blank | 35 comment | 27 complexity | 37cb2b79dfb4ce4e15d21e847c1d5c97 MD5 | raw file
Possible License(s): BSD-3-Clause, MIT
  1. <?php
  2. /**
  3. * Nette Framework
  4. *
  5. * @copyright Copyright (c) 2004, 2010 David Grudl
  6. * @license http://nettephp.com/license Nette license
  7. * @link http://nettephp.com
  8. * @category Nette
  9. * @package Nette\Reflection
  10. */
  11. /**
  12. * Annotations support for PHP.
  13. *
  14. * @copyright Copyright (c) 2004, 2010 David Grudl
  15. * @package Nette\Reflection
  16. * @Annotation
  17. */
  18. final class AnnotationsParser
  19. {
  20. /** @ignore internal single & double quoted PHP string */
  21. const RE_STRING = '\'(?:\\\\.|[^\'\\\\])*\'|"(?:\\\\.|[^"\\\\])*"';
  22. /** @ignore internal PHP identifier */
  23. const RE_IDENTIFIER = '[_a-zA-Z\x7F-\xFF][_a-zA-Z0-9\x7F-\xFF]*';
  24. /** @var bool */
  25. public static $useReflection;
  26. /** @var array */
  27. private static $cache;
  28. /** @var array */
  29. private static $timestamps;
  30. /**
  31. * Static class - cannot be instantiated.
  32. */
  33. final public function __construct()
  34. {
  35. throw new LogicException("Cannot instantiate static class " . get_class($this));
  36. }
  37. /**
  38. * Returns annotations.
  39. * @param ReflectionClass|\ReflectionMethod|\ReflectionProperty
  40. * @return array
  41. */
  42. public static function getAll(Reflector $r)
  43. {
  44. if ($r instanceof ReflectionClass) {
  45. $type = $r->getName();
  46. $member = '';
  47. } elseif ($r instanceof ReflectionMethod) {
  48. $type = $r->getDeclaringClass()->getName();
  49. $member = $r->getName();
  50. } else {
  51. $type = $r->getDeclaringClass()->getName();
  52. $member = '$' . $r->getName();
  53. }
  54. if (!self::$useReflection) { // auto-expire cache
  55. $file = $r instanceof ReflectionClass ? $r->getFileName() : $r->getDeclaringClass()->getFileName(); // will be used later
  56. if ($file && isset(self::$timestamps[$file]) && self::$timestamps[$file] !== filemtime($file)) {
  57. unset(self::$cache[$type]);
  58. }
  59. unset(self::$timestamps[$file]);
  60. }
  61. if (isset(self::$cache[$type][$member])) { // is value cached?
  62. return self::$cache[$type][$member];
  63. }
  64. if (self::$useReflection === NULL) { // detects whether is reflection available
  65. self::$useReflection = (bool) ClassReflection::from(__CLASS__)->getDocComment();
  66. }
  67. if (self::$useReflection) {
  68. return self::$cache[$type][$member] = self::parseComment($r->getDocComment());
  69. } else {
  70. if (self::$cache === NULL) {
  71. self::$cache = (array) self::getCache()->offsetGet('list');
  72. self::$timestamps = isset(self::$cache['*']) ? self::$cache['*'] : array();
  73. }
  74. if (!isset(self::$cache[$type]) && $file) {
  75. self::$cache['*'][$file] = filemtime($file);
  76. self::parseScript($file);
  77. self::getCache()->save('list', self::$cache);
  78. }
  79. if (isset(self::$cache[$type][$member])) {
  80. return self::$cache[$type][$member];
  81. } else {
  82. return self::$cache[$type][$member] = array();
  83. }
  84. }
  85. }
  86. /**
  87. * Parses phpDoc comment.
  88. * @param string
  89. * @return array
  90. */
  91. private static function parseComment($comment)
  92. {
  93. static $tokens = array('true' => TRUE, 'false' => FALSE, 'null' => NULL, '' => TRUE);
  94. preg_match_all('~
  95. @('.self::RE_IDENTIFIER.')[ \t]* ## annotation
  96. (
  97. \((?>'.self::RE_STRING.'|[^\'")@]+)+\)| ## (value)
  98. [^(@\r\n][^@\r\n]*|) ## value
  99. ~xi', trim($comment, '/*'), $matches, PREG_SET_ORDER);
  100. $res = array();
  101. foreach ($matches as $match)
  102. {
  103. list(, $name, $value) = $match;
  104. if (substr($value, 0, 1) === '(') {
  105. $items = array();
  106. $key = '';
  107. $val = TRUE;
  108. $value[0] = ',';
  109. while (preg_match('#\s*,\s*(?>('.self::RE_IDENTIFIER.')\s*=\s*)?('.self::RE_STRING.'|[^\'"),\s][^\'"),]*)#A', $value, $m)) {
  110. $value = substr($value, strlen($m[0]));
  111. list(, $key, $val) = $m;
  112. if ($val[0] === "'" || $val[0] === '"') {
  113. $val = substr($val, 1, -1);
  114. } elseif (is_numeric($val)) {
  115. $val = 1 * $val;
  116. } else {
  117. $lval = strtolower($val);
  118. $val = array_key_exists($lval, $tokens) ? $tokens[$lval] : $val;
  119. }
  120. if ($key === '') {
  121. $items[] = $val;
  122. } else {
  123. $items[$key] = $val;
  124. }
  125. }
  126. $value = count($items) < 2 && $key === '' ? $val : $items;
  127. } else {
  128. $value = trim($value);
  129. if (is_numeric($value)) {
  130. $value = 1 * $value;
  131. } else {
  132. $lval = strtolower($value);
  133. $value = array_key_exists($lval, $tokens) ? $tokens[$lval] : $value;
  134. }
  135. }
  136. $class = $name . 'Annotation';
  137. if (class_exists($class)) {
  138. $res[$name][] = new $class(is_array($value) ? $value : array('value' => $value));
  139. } else {
  140. $res[$name][] = is_array($value) ? new ArrayObject($value, ArrayObject::ARRAY_AS_PROPS) : $value;
  141. }
  142. }
  143. return $res;
  144. }
  145. /**
  146. * Parses PHP file.
  147. * @param string
  148. * @return void
  149. */
  150. private static function parseScript($file)
  151. {
  152. if (!defined('T_NAMESPACE')) {
  153. define('T_NAMESPACE', -1);
  154. define('T_NS_SEPARATOR', -1);
  155. }
  156. $s = file_get_contents($file);
  157. if (preg_match('#//nette'.'loader=(\S*)#', $s)) {
  158. return; // TODO: allways ignore?
  159. }
  160. $expected = $namespace = $class = $docComment = NULL;
  161. $level = $classLevel = 0;
  162. foreach (token_get_all($s) as $token)
  163. {
  164. if (is_array($token)) {
  165. switch ($token[0]) {
  166. case T_DOC_COMMENT:
  167. $docComment = $token[1];
  168. case T_WHITESPACE:
  169. case T_COMMENT:
  170. continue 2;
  171. case T_STRING:
  172. case T_NS_SEPARATOR:
  173. case T_VARIABLE:
  174. if ($expected) {
  175. $name .= $token[1];
  176. }
  177. continue 2;
  178. case T_FUNCTION:
  179. case T_VAR:
  180. case T_PUBLIC:
  181. case T_PROTECTED:
  182. case T_NAMESPACE:
  183. case T_CLASS:
  184. case T_INTERFACE:
  185. $expected = $token[0];
  186. $name = NULL;
  187. continue 2;
  188. case T_STATIC:
  189. case T_ABSTRACT:
  190. case T_FINAL:
  191. continue 2; // ignore in expectation
  192. case T_CURLY_OPEN:
  193. case T_DOLLAR_OPEN_CURLY_BRACES:
  194. $level++;
  195. }
  196. }
  197. if ($expected) {
  198. switch ($expected) {
  199. case T_CLASS:
  200. case T_INTERFACE:
  201. $class = $namespace . $name;
  202. $classLevel = $level;
  203. $name = '';
  204. // break intentionally omitted
  205. case T_FUNCTION:
  206. if ($token === '&') continue 2; // ignore
  207. case T_VAR:
  208. case T_PUBLIC:
  209. case T_PROTECTED:
  210. if ($class && $name !== NULL && $docComment) {
  211. self::$cache[$class][$name] = self::parseComment($docComment);
  212. }
  213. break;
  214. case T_NAMESPACE:
  215. $namespace = $name . '\\';
  216. }
  217. $expected = $docComment = NULL;
  218. }
  219. if ($token === ';') {
  220. $docComment = NULL;
  221. } elseif ($token === '{') {
  222. $docComment = NULL;
  223. $level++;
  224. } elseif ($token === '}') {
  225. $level--;
  226. if ($level === $classLevel) {
  227. $class = NULL;
  228. }
  229. }
  230. }
  231. }
  232. /********************* backend ****************d*g**/
  233. /**
  234. * @return Cache
  235. */
  236. protected static function getCache()
  237. {
  238. return Environment::getCache('Nette.Annotations');
  239. }
  240. }