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, 'object' => 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: return PHP_VERSION_ID >= 70000 && $func->hasReturnType()
42: ? self::normalizeType((string) $func->getReturnType(), $func)
43: : null;
44: }
45:
46:
47: 48: 49:
50: public static function getParameterType(\ReflectionParameter $param)
51: {
52: if (PHP_VERSION_ID >= 70000) {
53: return $param->hasType()
54: ? self::normalizeType((string) $param->getType(), $param)
55: : null;
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: private static function normalizeType($type, $reflection)
72: {
73: $lower = strtolower($type);
74: if ($lower === 'self') {
75: return $reflection->getDeclaringClass()->getName();
76: } elseif ($lower === 'parent' && $reflection->getDeclaringClass()->getParentClass()) {
77: return $reflection->getDeclaringClass()->getParentClass()->getName();
78: } else {
79: return $type;
80: }
81: }
82:
83:
84: 85: 86: 87:
88: public static function getParameterDefaultValue(\ReflectionParameter $param)
89: {
90: if ($param->isDefaultValueConstant()) {
91: $const = $orig = $param->getDefaultValueConstantName();
92: $pair = explode('::', $const);
93: if (isset($pair[1]) && strtolower($pair[0]) === 'self') {
94: $const = $param->getDeclaringClass()->getName() . '::' . $pair[1];
95: }
96: if (!defined($const)) {
97: $const = substr((string) strrchr($const, '\\'), 1);
98: if (isset($pair[1]) || !defined($const)) {
99: $name = self::toString($param);
100: throw new \ReflectionException("Unable to resolve constant $orig used as default value of $name.");
101: }
102: }
103: return constant($const);
104: }
105: return $param->getDefaultValue();
106: }
107:
108:
109: 110: 111: 112:
113: public static function getPropertyDeclaringClass(\ReflectionProperty $prop)
114: {
115: foreach ($prop->getDeclaringClass()->getTraits() as $trait) {
116: if ($trait->hasProperty($prop->getName())) {
117: return self::getPropertyDeclaringClass($trait->getProperty($prop->getName()));
118: }
119: }
120: return $prop->getDeclaringClass();
121: }
122:
123:
124: 125: 126: 127:
128: public static function ()
129: {
130: static $res;
131: return $res === null
132: ? $res = (bool) (new \ReflectionMethod(__METHOD__))->getDocComment()
133: : $res;
134: }
135:
136:
137: 138: 139:
140: public static function toString(\Reflector $ref)
141: {
142: if ($ref instanceof \ReflectionClass) {
143: return $ref->getName();
144: } elseif ($ref instanceof \ReflectionMethod) {
145: return $ref->getDeclaringClass()->getName() . '::' . $ref->getName();
146: } elseif ($ref instanceof \ReflectionFunction) {
147: return $ref->getName();
148: } elseif ($ref instanceof \ReflectionProperty) {
149: return self::getPropertyDeclaringClass($ref)->getName() . '::$' . $ref->getName();
150: } elseif ($ref instanceof \ReflectionParameter) {
151: return '$' . $ref->getName() . ' in ' . self::toString($ref->getDeclaringFunction()) . '()';
152: } else {
153: throw new Nette\InvalidArgumentException;
154: }
155: }
156:
157:
158: 159: 160: 161: 162: 163:
164: public static function expandClassName($name, \ReflectionClass $rc)
165: {
166: $lower = strtolower($name);
167: if (empty($name)) {
168: throw new Nette\InvalidArgumentException('Class name must not be empty.');
169:
170: } elseif (isset(self::$builtinTypes[$lower])) {
171: return $lower;
172:
173: } elseif ($lower === 'self') {
174: return $rc->getName();
175:
176: } elseif ($name[0] === '\\') {
177: return ltrim($name, '\\');
178: }
179:
180: $uses = self::getUseStatements($rc);
181: $parts = explode('\\', $name, 2);
182: if (isset($uses[$parts[0]])) {
183: $parts[0] = $uses[$parts[0]];
184: return implode('\\', $parts);
185:
186: } elseif ($rc->inNamespace()) {
187: return $rc->getNamespaceName() . '\\' . $name;
188:
189: } else {
190: return $name;
191: }
192: }
193:
194:
195: 196: 197:
198: public static function getUseStatements(\ReflectionClass $class)
199: {
200: static $cache = [];
201: if (!isset($cache[$name = $class->getName()])) {
202: if ($class->isInternal()) {
203: $cache[$name] = [];
204: } else {
205: $code = file_get_contents($class->getFileName());
206: $cache = self::parseUseStatements($code, $name) + $cache;
207: }
208: }
209: return $cache[$name];
210: }
211:
212:
213: 214: 215: 216: 217:
218: private static function parseUseStatements($code, $forClass = null)
219: {
220: $tokens = token_get_all($code);
221: $namespace = $class = $classLevel = $level = null;
222: $res = $uses = [];
223:
224: while ($token = current($tokens)) {
225: next($tokens);
226: switch (is_array($token) ? $token[0] : $token) {
227: case T_NAMESPACE:
228: $namespace = ltrim(self::fetch($tokens, [T_STRING, T_NS_SEPARATOR]) . '\\', '\\');
229: $uses = [];
230: break;
231:
232: case T_CLASS:
233: case T_INTERFACE:
234: case T_TRAIT:
235: if ($name = self::fetch($tokens, T_STRING)) {
236: $class = $namespace . $name;
237: $classLevel = $level + 1;
238: $res[$class] = $uses;
239: if ($class === $forClass) {
240: return $res;
241: }
242: }
243: break;
244:
245: case T_USE:
246: while (!$class && ($name = self::fetch($tokens, [T_STRING, T_NS_SEPARATOR]))) {
247: $name = ltrim($name, '\\');
248: if (self::fetch($tokens, '{')) {
249: while ($suffix = self::fetch($tokens, [T_STRING, T_NS_SEPARATOR])) {
250: if (self::fetch($tokens, T_AS)) {
251: $uses[self::fetch($tokens, T_STRING)] = $name . $suffix;
252: } else {
253: $tmp = explode('\\', $suffix);
254: $uses[end($tmp)] = $name . $suffix;
255: }
256: if (!self::fetch($tokens, ',')) {
257: break;
258: }
259: }
260:
261: } elseif (self::fetch($tokens, T_AS)) {
262: $uses[self::fetch($tokens, T_STRING)] = $name;
263:
264: } else {
265: $tmp = explode('\\', $name);
266: $uses[end($tmp)] = $name;
267: }
268: if (!self::fetch($tokens, ',')) {
269: break;
270: }
271: }
272: break;
273:
274: case T_CURLY_OPEN:
275: case T_DOLLAR_OPEN_CURLY_BRACES:
276: case '{':
277: $level++;
278: break;
279:
280: case '}':
281: if ($level === $classLevel) {
282: $class = $classLevel = null;
283: }
284: $level--;
285: }
286: }
287:
288: return $res;
289: }
290:
291:
292: private static function fetch(&$tokens, $take)
293: {
294: $res = null;
295: while ($token = current($tokens)) {
296: list($token, $s) = is_array($token) ? $token : [$token, $token];
297: if (in_array($token, (array) $take, true)) {
298: $res .= $s;
299: } elseif (!in_array($token, [T_DOC_COMMENT, T_WHITESPACE, T_COMMENT], true)) {
300: break;
301: }
302: next($tokens);
303: }
304: return $res;
305: }
306: }
307: