1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Atreyu; |
4
|
|
|
|
5
|
|
|
use phpDocumentor\Reflection\DocBlock; |
6
|
|
|
use phpDocumentor\Reflection\DocBlock\Context; |
7
|
|
|
|
8
|
|
|
class CachingReflector extends BaseReflector implements Reflector |
9
|
|
|
{ |
10
|
|
|
const CACHE_KEY_CLASSES = 'atreyu.refls.classes.'; |
11
|
|
|
const CACHE_KEY_CTORS = 'atreyu.refls.ctors.'; |
12
|
|
|
const CACHE_KEY_CTOR_PARAMS = 'atreyu.refls.ctor-params.'; |
13
|
|
|
const CACHE_KEY_FUNCS = 'atreyu.refls.funcs.'; |
14
|
|
|
const CACHE_KEY_METHODS = 'atreyu.refls.methods.'; |
15
|
|
|
const CACHE_KEY_DOC_BLOCK = 'atreyu.refls.doc-block.'; |
16
|
|
|
const CACHE_KEY_IMPLEMENTED = 'atreyu.refls.implemented.'; |
17
|
|
|
|
18
|
|
|
private $reflector; |
19
|
|
|
private $cache; |
20
|
|
|
|
21
|
|
|
public function __construct(Reflector $reflector = null, ReflectionCache $cache = null) |
22
|
|
|
{ |
23
|
|
|
$this->reflector = $reflector ?: new StandardReflector; |
24
|
|
|
$this->cache = $cache ?: new ReflectionCacheArray; |
25
|
|
|
} |
26
|
|
|
|
27
|
|
View Code Duplication |
public function getClass($class) |
|
|
|
|
28
|
|
|
{ |
29
|
|
|
$cacheKey = self::CACHE_KEY_CLASSES.strtolower($class); |
30
|
|
|
|
31
|
|
|
if (!$reflectionClass = $this->cache->fetch($cacheKey)) { |
32
|
|
|
$reflectionClass = new ExtendedReflectionClass($class); |
33
|
|
|
$this->cache->store($cacheKey, $reflectionClass); |
34
|
|
|
} |
35
|
|
|
|
36
|
|
|
return $reflectionClass; |
37
|
|
|
} |
38
|
|
|
|
39
|
|
View Code Duplication |
public function getCtor($class) |
|
|
|
|
40
|
|
|
{ |
41
|
|
|
$cacheKey = self::CACHE_KEY_CTORS.strtolower($class); |
42
|
|
|
|
43
|
|
|
$reflectedCtor = $this->cache->fetch($cacheKey); |
44
|
|
|
|
45
|
|
|
if ($reflectedCtor === false) { |
46
|
|
|
$reflectionClass = $this->getClass($class); |
47
|
|
|
$reflectedCtor = $reflectionClass->getConstructor(); |
48
|
|
|
$this->cache->store($cacheKey, $reflectedCtor); |
49
|
|
|
} |
50
|
|
|
|
51
|
|
|
return $reflectedCtor; |
52
|
|
|
} |
53
|
|
|
|
54
|
|
|
public function getCtorParams($class) |
55
|
|
|
{ |
56
|
|
|
$cacheKey = self::CACHE_KEY_CTOR_PARAMS.strtolower($class); |
57
|
|
|
|
58
|
|
|
$reflectedCtorParams = $this->cache->fetch($cacheKey); |
59
|
|
|
|
60
|
|
|
if (false !== $reflectedCtorParams) { |
61
|
|
|
return $reflectedCtorParams; |
62
|
|
|
} elseif ($reflectedCtor = $this->getCtor($class)) { |
63
|
|
|
$reflectedCtorParams = $reflectedCtor->getParameters(); |
64
|
|
|
} else { |
65
|
|
|
$reflectedCtorParams = null; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
$this->cache->store($cacheKey, $reflectedCtorParams); |
69
|
|
|
|
70
|
|
|
return $reflectedCtorParams; |
71
|
|
|
} |
72
|
|
|
|
73
|
|
|
public function getParamTypeHint(\ReflectionFunctionAbstract $function, \ReflectionParameter $param, array $arguments = []) |
74
|
|
|
{ |
75
|
|
|
$lowParam = strtolower($param->name); |
76
|
|
|
|
77
|
|
|
if ($function instanceof \ReflectionMethod) { |
78
|
|
|
$lowClass = strtolower($function->class); |
79
|
|
|
$lowMethod = strtolower($function->name); |
80
|
|
|
$paramCacheKey = self::CACHE_KEY_CLASSES."{$lowClass}.{$lowMethod}.param-{$lowParam}"; |
81
|
|
|
} else { |
82
|
|
|
$lowFunc = strtolower($function->name); |
83
|
|
|
$paramCacheKey = ($lowFunc !== '{closure}') |
84
|
|
|
? self::CACHE_KEY_FUNCS.".{$lowFunc}.param-{$lowParam}" |
85
|
|
|
: null; |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
$typeHint = ($paramCacheKey === null) ? false : $this->cache->fetch($paramCacheKey); |
89
|
|
|
|
90
|
|
|
if (false !== $typeHint) { |
91
|
|
|
return $typeHint; |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
if ($reflectionClass = $param->getClass()) { |
95
|
|
|
$typeHint = $reflectionClass->getName(); |
96
|
|
|
$classCacheKey = self::CACHE_KEY_CLASSES.strtolower($typeHint); |
97
|
|
|
$this->cache->store($classCacheKey, $this->getClass($param->getClass()->getName())); |
98
|
|
|
} elseif (($function instanceof \ReflectionMethod) |
99
|
|
|
&& ($docBlockParams = $this->getDocBlock($function)->getTagsByName('param')) |
100
|
|
|
&& !empty($docBlockParams) |
101
|
|
|
) { |
102
|
|
|
$typeHint = $this->getParamDocBlockHint($docBlockParams, $param, $arguments); |
103
|
|
|
|
104
|
|
|
// store the ExtendedReflectionClass in the cache |
105
|
|
|
if ($typeHint !== false) { |
106
|
|
|
|
107
|
|
|
$classCacheKey = self::CACHE_KEY_CLASSES.strtolower($typeHint); |
108
|
|
|
$this->cache->store($classCacheKey, $this->getClass($typeHint)); |
|
|
|
|
109
|
|
|
} |
110
|
|
|
} else { |
111
|
|
|
$typeHint = null; |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
$this->cache->store($paramCacheKey, $typeHint); |
115
|
|
|
|
116
|
|
|
return $typeHint; |
117
|
|
|
} |
118
|
|
|
|
119
|
|
View Code Duplication |
public function getFunction($functionName) |
|
|
|
|
120
|
|
|
{ |
121
|
|
|
$lowFunc = strtolower($functionName); |
122
|
|
|
$cacheKey = self::CACHE_KEY_FUNCS.$lowFunc; |
123
|
|
|
|
124
|
|
|
$reflectedFunc = $this->cache->fetch($cacheKey); |
125
|
|
|
|
126
|
|
|
if (false === $reflectedFunc) { |
127
|
|
|
$reflectedFunc = new \ReflectionFunction($functionName); |
128
|
|
|
$this->cache->store($cacheKey, $reflectedFunc); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
return $reflectedFunc; |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
public function getMethod($classNameOrInstance, $methodName) |
135
|
|
|
{ |
136
|
|
|
$className = is_string($classNameOrInstance) |
137
|
|
|
? $classNameOrInstance |
138
|
|
|
: get_class($classNameOrInstance); |
139
|
|
|
|
140
|
|
|
$cacheKey = self::CACHE_KEY_METHODS.strtolower($className).'.'.strtolower($methodName); |
141
|
|
|
|
142
|
|
|
if (!$reflectedMethod = $this->cache->fetch($cacheKey)) { |
143
|
|
|
$reflectedMethod = new \ReflectionMethod($className, $methodName); |
144
|
|
|
$this->cache->store($cacheKey, $reflectedMethod); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
return $reflectedMethod; |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
public function getDocBlock(\ReflectionMethod $method) |
151
|
|
|
{ |
152
|
|
|
$cacheKey = self::CACHE_KEY_DOC_BLOCK.strtolower($method->class); |
153
|
|
|
if (!$docBlock = $this->cache->fetch($cacheKey)) { |
154
|
|
|
|
155
|
|
|
$class = $this->getClass($method->class); |
156
|
|
|
|
157
|
|
|
$docBlock = new DocBlock( |
158
|
|
|
$method->getDocComment(), |
159
|
|
|
new Context( |
160
|
|
|
$class->getNamespaceName(), |
161
|
|
|
$class->getUseStatements() |
162
|
|
|
) |
163
|
|
|
); |
164
|
|
|
|
165
|
|
|
$this->cache->store($cacheKey, $docBlock); |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
return $docBlock; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
/** |
172
|
|
|
* @param $className |
173
|
|
|
* |
174
|
|
|
* @return array|bool |
175
|
|
|
*/ |
176
|
|
View Code Duplication |
public function getImplemented($className) |
|
|
|
|
177
|
|
|
{ |
178
|
|
|
$cacheKey = self::CACHE_KEY_IMPLEMENTED.strtolower($className); |
179
|
|
|
|
180
|
|
|
if (!$implemented = $this->cache->fetch($cacheKey)) { |
181
|
|
|
$implemented = parent::getImplemented($className); |
182
|
|
|
$this->cache->store($cacheKey, $implemented); |
183
|
|
|
} |
184
|
|
|
|
185
|
|
|
return $implemented; |
186
|
|
|
} |
187
|
|
|
} |
188
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.