1: <?php
2:
3: 4: 5: 6:
7:
8: namespace Nette\Utils;
9:
10: use Nette;
11:
12:
13: 14: 15:
16: class Reflection
17: {
18: use Nette\StaticClass;
19:
20: private static $builtinTypes = [
21: 'string' => 1, 'int' => 1, 'float' => 1, 'bool' => 1, 'array' => 1,
22: 'callable' => 1, 'iterable' => 1, 'void' => 1
23: ];
24:
25:
26: 27: 28: 29:
30: public static function isBuiltinType($type)
31: {
32: return isset(self::$builtinTypes[strtolower($type)]);
33: }
34:
35:
36: 37: 38:
39: public static function getReturnType(\ReflectionFunctionAbstract $func)
40: {
41: if (PHP_VERSION_ID >= 70000 && $func->hasReturnType()) {
42: $type = (string) $func->getReturnType();
43: return strtolower($type) === 'self' ? $func->getDeclaringClass()->getName() : $type;
44: }
45: }
46:
47:
48: 49: 50:
51: public static function getParameterType(\ReflectionParameter $param)
52: {
53: if (PHP_VERSION_ID >= 70000) {
54: $type = $param->hasType() ? (string) $param->getType() : NULL;
55: return strtolower($type) === 'self' ? $param->getDeclaringClass()->getName() : $type;
56: } elseif ($param->isArray() || $param->isCallable()) {
57: return $param->isArray() ? 'array' : 'callable';
58: } else {
59: try {
60: return ($ref = $param->getClass()) ? $ref->getName() : NULL;
61: } catch (\ReflectionException $e) {
62: if (preg_match('#Class (.+) does not exist#', $e->getMessage(), $m)) {
63: return $m[1];
64: }
65: throw $e;
66: }
67: }
68: }
69:
70:
71: 72: 73: 74:
75: public static function getParameterDefaultValue(\ReflectionParameter $param)
76: {
77: if ($param->isDefaultValueConstant()) {
78: $const = $orig = $param->getDefaultValueConstantName();
79: $pair = explode('::', $const);
80: if (isset($pair[1]) && strtolower($pair[0]) === 'self') {
81: $const = $param->getDeclaringClass()->getName() . '::' . $pair[1];
82: }
83: if (!defined($const)) {
84: $const = substr((string) strrchr($const, '\\'), 1);
85: if (isset($pair[1]) || !defined($const)) {
86: $name = self::toString($param);
87: throw new \ReflectionException("Unable to resolve constant $orig used as default value of $name.");
88: }
89: }
90: return constant($const);
91: }
92: return $param->getDefaultValue();
93: }
94:
95:
96: 97: 98: 99:
100: public static function getPropertyDeclaringClass(\ReflectionProperty $prop)
101: {
102: foreach ($prop->getDeclaringClass()->getTraits() as $trait) {
103: if ($trait->hasProperty($prop->getName())) {
104: return self::getPropertyDeclaringClass($trait->getProperty($prop->getName()));
105: }
106: }
107: return $prop->getDeclaringClass();
108: }
109:
110:
111: 112: 113: 114:
115: public static function ()
116: {
117: static $res;
118: return $res === NULL
119: ? $res = (bool) (new \ReflectionMethod(__METHOD__))->getDocComment()
120: : $res;
121: }
122:
123:
124: 125: 126:
127: public static function toString(\Reflector $ref)
128: {
129: if ($ref instanceof \ReflectionClass) {
130: return $ref->getName();
131: } elseif ($ref instanceof \ReflectionMethod) {
132: return $ref->getDeclaringClass()->getName() . '::' . $ref->getName();
133: } elseif ($ref instanceof \ReflectionFunction) {
134: return $ref->getName();
135: } elseif ($ref instanceof \ReflectionProperty) {
136: return self::getPropertyDeclaringClass($ref)->getName() . '::$' . $ref->getName();
137: } elseif ($ref instanceof \ReflectionParameter) {
138: return '$' . $ref->getName() . ' in ' . self::toString($ref->getDeclaringFunction()) . '()';
139: } else {
140: throw new Nette\InvalidArgumentException;
141: }
142: }
143:
144:
145: 146: 147: 148: 149: 150:
151: public static function expandClassName($name, \ReflectionClass $rc)
152: {
153: $lower = strtolower($name);
154: if (empty($name)) {
155: throw new Nette\InvalidArgumentException('Class name must not be empty.');
156:
157: } elseif (isset(self::$builtinTypes[$lower])) {
158: return $lower;
159:
160: } elseif ($lower === 'self') {
161: return $rc->getName();
162:
163: } elseif ($name[0] === '\\') {
164: return ltrim($name, '\\');
165: }
166:
167: $uses = self::getUseStatements($rc);
168: $parts = explode('\\', $name, 2);
169: if (isset($uses[$parts[0]])) {
170: $parts[0] = $uses[$parts[0]];
171: return implode('\\', $parts);
172:
173: } elseif ($rc->inNamespace()) {
174: return $rc->getNamespaceName() . '\\' . $name;
175:
176: } else {
177: return $name;
178: }
179: }
180:
181:
182: 183: 184:
185: public static function getUseStatements(\ReflectionClass $class)
186: {
187: static $cache = [];
188: if (!isset($cache[$name = $class->getName()])) {
189: if ($class->isInternal()) {
190: $cache[$name] = [];
191: } else {
192: $code = file_get_contents($class->getFileName());
193: $cache = self::parseUseStatements($code, $name) + $cache;
194: }
195: }
196: return $cache[$name];
197: }
198:
199:
200: 201: 202: 203: 204:
205: private static function parseUseStatements($code, $forClass = NULL)
206: {
207: $tokens = token_get_all($code);
208: $namespace = $class = $classLevel = $level = NULL;
209: $res = $uses = [];
210:
211: while (list(, $token) = each($tokens)) {
212: switch (is_array($token) ? $token[0] : $token) {
213: case T_NAMESPACE:
214: $namespace = ltrim(self::fetch($tokens, [T_STRING, T_NS_SEPARATOR]) . '\\', '\\');
215: $uses = [];
216: break;
217:
218: case T_CLASS:
219: case T_INTERFACE:
220: case T_TRAIT:
221: if ($name = self::fetch($tokens, T_STRING)) {
222: $class = $namespace . $name;
223: $classLevel = $level + 1;
224: $res[$class] = $uses;
225: if ($class === $forClass) {
226: return $res;
227: }
228: }
229: break;
230:
231: case T_USE:
232: while (!$class && ($name = self::fetch($tokens, [T_STRING, T_NS_SEPARATOR]))) {
233: $name = ltrim($name, '\\');
234: if (self::fetch($tokens, '{')) {
235: while ($suffix = self::fetch($tokens, [T_STRING, T_NS_SEPARATOR])) {
236: if (self::fetch($tokens, T_AS)) {
237: $uses[self::fetch($tokens, T_STRING)] = $name . $suffix;
238: } else {
239: $tmp = explode('\\', $suffix);
240: $uses[end($tmp)] = $name . $suffix;
241: }
242: if (!self::fetch($tokens, ',')) {
243: break;
244: }
245: }
246:
247: } elseif (self::fetch($tokens, T_AS)) {
248: $uses[self::fetch($tokens, T_STRING)] = $name;
249:
250: } else {
251: $tmp = explode('\\', $name);
252: $uses[end($tmp)] = $name;
253: }
254: if (!self::fetch($tokens, ',')) {
255: break;
256: }
257: }
258: break;
259:
260: case T_CURLY_OPEN:
261: case T_DOLLAR_OPEN_CURLY_BRACES:
262: case '{':
263: $level++;
264: break;
265:
266: case '}':
267: if ($level === $classLevel) {
268: $class = $classLevel = NULL;
269: }
270: $level--;
271: }
272: }
273:
274: return $res;
275: }
276:
277:
278: private static function fetch(&$tokens, $take)
279: {
280: $res = NULL;
281: while ($token = current($tokens)) {
282: list($token, $s) = is_array($token) ? $token : [$token, $token];
283: if (in_array($token, (array) $take, TRUE)) {
284: $res .= $s;
285: } elseif (!in_array($token, [T_DOC_COMMENT, T_WHITESPACE, T_COMMENT], TRUE)) {
286: break;
287: }
288: next($tokens);
289: }
290: return $res;
291: }
292:
293: }
294: