/include/lib/TokenReflection/Resolver.php

https://github.com/eugenix/phpreflector · PHP · 278 lines · 191 code · 23 blank · 64 comment · 45 complexity · 0038ca1e9705d71546b636a6258b8c5d MD5 · raw file

  1. <?php
  2. /**
  3. * PHP Token Reflection
  4. *
  5. * Version 1.0.2
  6. *
  7. * LICENSE
  8. *
  9. * This source file is subject to the new BSD license that is bundled
  10. * with this library in the file license.txt.
  11. *
  12. * @author Ondřej Nešpor
  13. * @author Jaroslav Hanslík
  14. */
  15. namespace TokenReflection;
  16. /**
  17. * TokenReflection Resolver class.
  18. */
  19. class Resolver
  20. {
  21. /**
  22. * Placeholder for non-existen constants.
  23. *
  24. * @var null
  25. */
  26. const CONSTANT_NOT_FOUND = '~~NOT RESOLVED~~';
  27. /**
  28. * Constructor.
  29. *
  30. * Prevents from creating instances.
  31. *
  32. * @throws LogicException When trying to create a class instance.
  33. */
  34. final public function __construct()
  35. {
  36. throw new \LogicException('Static class cannot be instantiated.');
  37. }
  38. /**
  39. * Returns a fully qualified name of a class using imported/aliased namespaces.
  40. *
  41. * @param string $className Input class name
  42. * @param array $aliases Namespace import aliases
  43. * @param string $namespaceName Context namespace name
  44. * @return string
  45. */
  46. final public static function resolveClassFQN($className, array $aliases, $namespaceName = null)
  47. {
  48. if ($className{0} == '\\') {
  49. // FQN
  50. return ltrim($className, '\\');
  51. }
  52. if (false === ($position = strpos($className, '\\'))) {
  53. // Plain class name
  54. if (isset($aliases[$className])) {
  55. return $aliases[$className];
  56. }
  57. } else {
  58. // Namespaced class name
  59. $alias = substr($className, 0, $position);
  60. if (isset($aliases[$alias])) {
  61. return $aliases[$alias] . '\\' . substr($className, $position + 1);
  62. }
  63. }
  64. return null === $namespaceName || '' === $namespaceName || $namespaceName === ReflectionNamespace::NO_NAMESPACE_NAME ? $className : $namespaceName . '\\' . $className;
  65. }
  66. /**
  67. * Returns a property/parameter/constant/static variable value definition.
  68. *
  69. * @param array $tokens Tokenized definition
  70. * @param \TokenReflection\ReflectionElement $reflection Caller reflection
  71. * @return string
  72. * @throws \TokenReflection\Exception\Runtime If an invalid reflection object was provided.
  73. * @throws \TokenReflection\Exception\Runtime If an invalid source code was provided.
  74. */
  75. final public static function getValueDefinition(array $tokens, ReflectionElement $reflection)
  76. {
  77. if ($reflection instanceof ReflectionConstant || $reflection instanceof ReflectionFunction) {
  78. $namespace = $reflection->getNamespaceName();
  79. } elseif ($reflection instanceof ReflectionParameter) {
  80. $namespace = $reflection->getDeclaringFunction()->getNamespaceName();
  81. } elseif ($reflection instanceof ReflectionProperty || $reflection instanceof ReflectionMethod) {
  82. $namespace = $reflection->getDeclaringClass()->getNamespaceName();
  83. } else {
  84. throw new Exception\Runtime(sprintf('Invalid reflection object given: "%s" ("%s")', get_class($reflection), $reflection->getName()), Exception\Runtime::INVALID_ARGUMENT);
  85. }
  86. // Process __LINE__ constants; replace with the line number of the corresponding token
  87. foreach ($tokens as $index => $token) {
  88. if (T_LINE === $token[0]) {
  89. $tokens[$index] = array(
  90. T_LNUMBER,
  91. $token[2],
  92. $token[2]
  93. );
  94. }
  95. }
  96. $source = self::getSourceCode($tokens);
  97. $constants = self::findConstants($tokens, $reflection);
  98. if (!empty($constants)) {
  99. foreach (array_reverse($constants, true) as $offset => $constant) {
  100. $value = '';
  101. try {
  102. switch ($constant) {
  103. case '__LINE__':
  104. throw new Exception\Runtime('__LINE__ constant cannot be resolved this way.', Exception\Runtime::INVALID_ARGUMENT);
  105. case '__FILE__':
  106. $value = $reflection->getFileName();
  107. break;
  108. case '__DIR__':
  109. $value = dirname($reflection->getFileName());
  110. break;
  111. case '__FUNCTION__':
  112. if ($reflection instanceof IReflectionParameter) {
  113. $value = $reflection->getDeclaringFunctionName();
  114. } elseif ($reflection instanceof IReflectionFunctionBase) {
  115. $value = $reflection->getName();
  116. }
  117. break;
  118. case '__CLASS__':
  119. if ($reflection instanceof IReflectionConstant || $reflection instanceof IReflectionParameter || $reflection instanceof IReflectionProperty || $reflection instanceof IReflectionMethod) {
  120. $value = $reflection->getDeclaringClassName() ?: '';
  121. }
  122. break;
  123. case '__TRAIT__':
  124. if ($reflection instanceof IReflectionMethod || $reflection instanceof IReflectionProperty) {
  125. $value = $reflection->getDeclaringTraitName() ?: '';
  126. } elseif ($reflection instanceof IReflectionParameter) {
  127. $method = $reflection->getDeclaringFunction();
  128. if ($method instanceof IReflectionMethod) {
  129. $value = $method->getDeclaringTraitName() ?: '';
  130. }
  131. }
  132. break;
  133. case '__METHOD__':
  134. if ($reflection instanceof IReflectionParameter) {
  135. if (null !== $reflection->getDeclaringClassName()) {
  136. $value = $reflection->getDeclaringClassName() . '::' . $reflection->getDeclaringFunctionName();
  137. } else {
  138. $value = $reflection->getDeclaringFunctionName();
  139. }
  140. } elseif ($reflection instanceof IReflectionConstant || $reflection instanceof IReflectionProperty) {
  141. $value = $reflection->getDeclaringClassName() ?: '';
  142. } elseif ($reflection instanceof IReflectionMethod) {
  143. $value = $reflection->getDeclaringClassName() . '::' . $reflection->getName();
  144. } elseif ($reflection instanceof IReflectionFunction) {
  145. $value = $reflection->getName();
  146. }
  147. break;
  148. case '__NAMESPACE__':
  149. if (($reflection instanceof IReflectionConstant && null !== $reflection->getDeclaringClassName()) || $reflection instanceof IReflectionProperty) {
  150. $value = $reflection->getDeclaringClass()->getNamespaceName();
  151. } elseif ($reflection instanceof IReflectionParameter) {
  152. if (null !== $reflection->getDeclaringClassName()) {
  153. $value = $reflection->getDeclaringClass()->getNamespaceName();
  154. } else {
  155. $value = $reflection->getDeclaringFunction()->getNamespaceName();
  156. }
  157. } elseif ($reflection instanceof IReflectionMethod) {
  158. $value = $reflection->getDeclaringClass()->getNamespaceName();
  159. } else {
  160. $value = $reflection->getNamespaceName();
  161. }
  162. break;
  163. default:
  164. if (0 === stripos($constant, 'self::') || 0 === stripos($constant, 'parent::')) {
  165. // Handle self:: and parent:: definitions
  166. if ($reflection instanceof ReflectionConstant) {
  167. throw new Exception\Runtime('Constants cannot use self:: and parent:: references.', Exception\Runtime::INVALID_ARGUMENT);
  168. } elseif ($reflection instanceof ReflectionParameter && null === $reflection->getDeclaringClassName()) {
  169. throw new Exception\Runtime('Function parameters cannot use self:: and parent:: references.', Exception\Runtime::INVALID_ARGUMENT);
  170. }
  171. if (0 === stripos($constant, 'self::')) {
  172. $className = $reflection->getDeclaringClassName();
  173. } else {
  174. $declaringClass = $reflection->getDeclaringClass();
  175. $className = $declaringClass->getParentClassName() ?: self::CONSTANT_NOT_FOUND;
  176. }
  177. $constantName = $className . substr($constant, strpos($constant, '::'));
  178. } else {
  179. $constantName = self::resolveClassFQN($constant, $reflection->getNamespaceAliases(), $namespace);
  180. if ($cnt = strspn($constant, '\\')) {
  181. $constantName = str_repeat('\\', $cnt) . $constantName;
  182. }
  183. }
  184. $reflection = $reflection->getBroker()->getConstant($constantName);
  185. $value = $reflection->getValue();
  186. }
  187. } catch (Exception\Runtime $e) {
  188. $value = self::CONSTANT_NOT_FOUND;
  189. }
  190. $source = substr_replace($source, var_export($value, true), $offset, strlen($constant));
  191. }
  192. }
  193. return eval(sprintf('return %s;', $source));
  194. }
  195. /**
  196. * Returns a part of the source code defined by given tokens.
  197. *
  198. * @param array $tokens Tokens array
  199. * @return array
  200. */
  201. final public static function getSourceCode(array $tokens)
  202. {
  203. if (empty($tokens)) {
  204. return null;
  205. }
  206. $source = '';
  207. foreach ($tokens as $token) {
  208. $source .= $token[1];
  209. }
  210. return $source;
  211. }
  212. /**
  213. * Finds constant names in the token definition.
  214. *
  215. * @param array $tokens Tokenized source code
  216. * @param \TokenReflection\ReflectionElement $reflection Caller reflection
  217. * @return array
  218. */
  219. final public static function findConstants(array $tokens, ReflectionElement $reflection)
  220. {
  221. static $accepted = array(
  222. T_DOUBLE_COLON => true,
  223. T_STRING => true,
  224. T_NS_SEPARATOR => true,
  225. T_CLASS_C => true,
  226. T_DIR => true,
  227. T_FILE => true,
  228. T_LINE => true,
  229. T_FUNC_C => true,
  230. T_METHOD_C => true,
  231. T_NS_C => true,
  232. T_TRAIT_C => true
  233. );
  234. static $dontResolve = array('true' => true, 'false' => true, 'null' => true);
  235. // Adding a dummy token to the end
  236. $tokens[] = array(null);
  237. $constants = array();
  238. $constant = '';
  239. $offset = 0;
  240. foreach ($tokens as $token) {
  241. if (isset($accepted[$token[0]])) {
  242. $constant .= $token[1];
  243. } elseif ('' !== $constant) {
  244. if (!isset($dontResolve[strtolower($constant)])) {
  245. $constants[$offset - strlen($constant)] = $constant;
  246. }
  247. $constant = '';
  248. }
  249. if (null !== $token[0]) {
  250. $offset += strlen($token[1]);
  251. }
  252. }
  253. return $constants;
  254. }
  255. }