Chris@0
|
1 <?php
|
Chris@0
|
2 /*
|
Chris@0
|
3 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
Chris@0
|
4 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
Chris@0
|
5 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
Chris@0
|
6 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
Chris@0
|
7 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
Chris@0
|
8 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
Chris@0
|
9 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
Chris@0
|
10 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
Chris@0
|
11 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
Chris@0
|
12 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
Chris@0
|
13 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
Chris@0
|
14 *
|
Chris@0
|
15 * This software consists of voluntary contributions made by many individuals
|
Chris@0
|
16 * and is licensed under the MIT license. For more information, see
|
Chris@0
|
17 * <http://www.doctrine-project.org>.
|
Chris@0
|
18 */
|
Chris@0
|
19
|
Chris@0
|
20 namespace Doctrine\Common\Annotations;
|
Chris@0
|
21
|
Chris@0
|
22 use Doctrine\Common\Cache\Cache;
|
Chris@12
|
23 use ReflectionClass;
|
Chris@0
|
24
|
Chris@0
|
25 /**
|
Chris@0
|
26 * A cache aware annotation reader.
|
Chris@0
|
27 *
|
Chris@0
|
28 * @author Johannes M. Schmitt <schmittjoh@gmail.com>
|
Chris@0
|
29 * @author Benjamin Eberlei <kontakt@beberlei.de>
|
Chris@0
|
30 */
|
Chris@0
|
31 final class CachedReader implements Reader
|
Chris@0
|
32 {
|
Chris@0
|
33 /**
|
Chris@0
|
34 * @var Reader
|
Chris@0
|
35 */
|
Chris@0
|
36 private $delegate;
|
Chris@0
|
37
|
Chris@0
|
38 /**
|
Chris@0
|
39 * @var Cache
|
Chris@0
|
40 */
|
Chris@0
|
41 private $cache;
|
Chris@0
|
42
|
Chris@0
|
43 /**
|
Chris@0
|
44 * @var boolean
|
Chris@0
|
45 */
|
Chris@0
|
46 private $debug;
|
Chris@0
|
47
|
Chris@0
|
48 /**
|
Chris@0
|
49 * @var array
|
Chris@0
|
50 */
|
Chris@0
|
51 private $loadedAnnotations = array();
|
Chris@0
|
52
|
Chris@0
|
53 /**
|
Chris@0
|
54 * Constructor.
|
Chris@0
|
55 *
|
Chris@0
|
56 * @param Reader $reader
|
Chris@0
|
57 * @param Cache $cache
|
Chris@0
|
58 * @param bool $debug
|
Chris@0
|
59 */
|
Chris@0
|
60 public function __construct(Reader $reader, Cache $cache, $debug = false)
|
Chris@0
|
61 {
|
Chris@0
|
62 $this->delegate = $reader;
|
Chris@0
|
63 $this->cache = $cache;
|
Chris@0
|
64 $this->debug = (boolean) $debug;
|
Chris@0
|
65 }
|
Chris@0
|
66
|
Chris@0
|
67 /**
|
Chris@0
|
68 * {@inheritDoc}
|
Chris@0
|
69 */
|
Chris@12
|
70 public function getClassAnnotations(ReflectionClass $class)
|
Chris@0
|
71 {
|
Chris@0
|
72 $cacheKey = $class->getName();
|
Chris@0
|
73
|
Chris@0
|
74 if (isset($this->loadedAnnotations[$cacheKey])) {
|
Chris@0
|
75 return $this->loadedAnnotations[$cacheKey];
|
Chris@0
|
76 }
|
Chris@0
|
77
|
Chris@0
|
78 if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
|
Chris@0
|
79 $annots = $this->delegate->getClassAnnotations($class);
|
Chris@0
|
80 $this->saveToCache($cacheKey, $annots);
|
Chris@0
|
81 }
|
Chris@0
|
82
|
Chris@0
|
83 return $this->loadedAnnotations[$cacheKey] = $annots;
|
Chris@0
|
84 }
|
Chris@0
|
85
|
Chris@0
|
86 /**
|
Chris@0
|
87 * {@inheritDoc}
|
Chris@0
|
88 */
|
Chris@12
|
89 public function getClassAnnotation(ReflectionClass $class, $annotationName)
|
Chris@0
|
90 {
|
Chris@0
|
91 foreach ($this->getClassAnnotations($class) as $annot) {
|
Chris@0
|
92 if ($annot instanceof $annotationName) {
|
Chris@0
|
93 return $annot;
|
Chris@0
|
94 }
|
Chris@0
|
95 }
|
Chris@0
|
96
|
Chris@0
|
97 return null;
|
Chris@0
|
98 }
|
Chris@0
|
99
|
Chris@0
|
100 /**
|
Chris@0
|
101 * {@inheritDoc}
|
Chris@0
|
102 */
|
Chris@0
|
103 public function getPropertyAnnotations(\ReflectionProperty $property)
|
Chris@0
|
104 {
|
Chris@0
|
105 $class = $property->getDeclaringClass();
|
Chris@0
|
106 $cacheKey = $class->getName().'$'.$property->getName();
|
Chris@0
|
107
|
Chris@0
|
108 if (isset($this->loadedAnnotations[$cacheKey])) {
|
Chris@0
|
109 return $this->loadedAnnotations[$cacheKey];
|
Chris@0
|
110 }
|
Chris@0
|
111
|
Chris@0
|
112 if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
|
Chris@0
|
113 $annots = $this->delegate->getPropertyAnnotations($property);
|
Chris@0
|
114 $this->saveToCache($cacheKey, $annots);
|
Chris@0
|
115 }
|
Chris@0
|
116
|
Chris@0
|
117 return $this->loadedAnnotations[$cacheKey] = $annots;
|
Chris@0
|
118 }
|
Chris@0
|
119
|
Chris@0
|
120 /**
|
Chris@0
|
121 * {@inheritDoc}
|
Chris@0
|
122 */
|
Chris@0
|
123 public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
|
Chris@0
|
124 {
|
Chris@0
|
125 foreach ($this->getPropertyAnnotations($property) as $annot) {
|
Chris@0
|
126 if ($annot instanceof $annotationName) {
|
Chris@0
|
127 return $annot;
|
Chris@0
|
128 }
|
Chris@0
|
129 }
|
Chris@0
|
130
|
Chris@0
|
131 return null;
|
Chris@0
|
132 }
|
Chris@0
|
133
|
Chris@0
|
134 /**
|
Chris@0
|
135 * {@inheritDoc}
|
Chris@0
|
136 */
|
Chris@0
|
137 public function getMethodAnnotations(\ReflectionMethod $method)
|
Chris@0
|
138 {
|
Chris@0
|
139 $class = $method->getDeclaringClass();
|
Chris@0
|
140 $cacheKey = $class->getName().'#'.$method->getName();
|
Chris@0
|
141
|
Chris@0
|
142 if (isset($this->loadedAnnotations[$cacheKey])) {
|
Chris@0
|
143 return $this->loadedAnnotations[$cacheKey];
|
Chris@0
|
144 }
|
Chris@0
|
145
|
Chris@0
|
146 if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
|
Chris@0
|
147 $annots = $this->delegate->getMethodAnnotations($method);
|
Chris@0
|
148 $this->saveToCache($cacheKey, $annots);
|
Chris@0
|
149 }
|
Chris@0
|
150
|
Chris@0
|
151 return $this->loadedAnnotations[$cacheKey] = $annots;
|
Chris@0
|
152 }
|
Chris@0
|
153
|
Chris@0
|
154 /**
|
Chris@0
|
155 * {@inheritDoc}
|
Chris@0
|
156 */
|
Chris@0
|
157 public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
|
Chris@0
|
158 {
|
Chris@0
|
159 foreach ($this->getMethodAnnotations($method) as $annot) {
|
Chris@0
|
160 if ($annot instanceof $annotationName) {
|
Chris@0
|
161 return $annot;
|
Chris@0
|
162 }
|
Chris@0
|
163 }
|
Chris@0
|
164
|
Chris@0
|
165 return null;
|
Chris@0
|
166 }
|
Chris@0
|
167
|
Chris@0
|
168 /**
|
Chris@0
|
169 * Clears loaded annotations.
|
Chris@0
|
170 *
|
Chris@0
|
171 * @return void
|
Chris@0
|
172 */
|
Chris@0
|
173 public function clearLoadedAnnotations()
|
Chris@0
|
174 {
|
Chris@0
|
175 $this->loadedAnnotations = array();
|
Chris@0
|
176 }
|
Chris@0
|
177
|
Chris@0
|
178 /**
|
Chris@0
|
179 * Fetches a value from the cache.
|
Chris@0
|
180 *
|
Chris@12
|
181 * @param string $cacheKey The cache key.
|
Chris@12
|
182 * @param ReflectionClass $class The related class.
|
Chris@0
|
183 *
|
Chris@0
|
184 * @return mixed The cached value or false when the value is not in cache.
|
Chris@0
|
185 */
|
Chris@12
|
186 private function fetchFromCache($cacheKey, ReflectionClass $class)
|
Chris@0
|
187 {
|
Chris@0
|
188 if (($data = $this->cache->fetch($cacheKey)) !== false) {
|
Chris@0
|
189 if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) {
|
Chris@0
|
190 return $data;
|
Chris@0
|
191 }
|
Chris@0
|
192 }
|
Chris@0
|
193
|
Chris@0
|
194 return false;
|
Chris@0
|
195 }
|
Chris@0
|
196
|
Chris@0
|
197 /**
|
Chris@0
|
198 * Saves a value to the cache.
|
Chris@0
|
199 *
|
Chris@12
|
200 * @param string $cacheKey The cache key.
|
Chris@12
|
201 * @param mixed $value The value.
|
Chris@0
|
202 *
|
Chris@0
|
203 * @return void
|
Chris@0
|
204 */
|
Chris@12
|
205 private function saveToCache($cacheKey, $value)
|
Chris@0
|
206 {
|
Chris@0
|
207 $this->cache->save($cacheKey, $value);
|
Chris@0
|
208 if ($this->debug) {
|
Chris@0
|
209 $this->cache->save('[C]'.$cacheKey, time());
|
Chris@0
|
210 }
|
Chris@0
|
211 }
|
Chris@0
|
212
|
Chris@0
|
213 /**
|
Chris@0
|
214 * Checks if the cache is fresh.
|
Chris@0
|
215 *
|
Chris@0
|
216 * @param string $cacheKey
|
Chris@12
|
217 * @param ReflectionClass $class
|
Chris@0
|
218 *
|
Chris@0
|
219 * @return boolean
|
Chris@0
|
220 */
|
Chris@12
|
221 private function isCacheFresh($cacheKey, ReflectionClass $class)
|
Chris@0
|
222 {
|
Chris@12
|
223 if (null === $lastModification = $this->getLastModification($class)) {
|
Chris@0
|
224 return true;
|
Chris@0
|
225 }
|
Chris@0
|
226
|
Chris@12
|
227 return $this->cache->fetch('[C]'.$cacheKey) >= $lastModification;
|
Chris@12
|
228 }
|
Chris@12
|
229
|
Chris@12
|
230 /**
|
Chris@12
|
231 * Returns the time the class was last modified, testing traits and parents
|
Chris@12
|
232 *
|
Chris@12
|
233 * @param ReflectionClass $class
|
Chris@12
|
234 * @return int
|
Chris@12
|
235 */
|
Chris@12
|
236 private function getLastModification(ReflectionClass $class)
|
Chris@12
|
237 {
|
Chris@12
|
238 $filename = $class->getFileName();
|
Chris@12
|
239 $parent = $class->getParentClass();
|
Chris@12
|
240
|
Chris@12
|
241 return max(array_merge(
|
Chris@12
|
242 [$filename ? filemtime($filename) : 0],
|
Chris@12
|
243 array_map([$this, 'getTraitLastModificationTime'], $class->getTraits()),
|
Chris@12
|
244 array_map([$this, 'getLastModification'], $class->getInterfaces()),
|
Chris@12
|
245 $parent ? [$this->getLastModification($parent)] : []
|
Chris@12
|
246 ));
|
Chris@12
|
247 }
|
Chris@12
|
248
|
Chris@12
|
249 /**
|
Chris@12
|
250 * @param ReflectionClass $reflectionTrait
|
Chris@12
|
251 * @return int
|
Chris@12
|
252 */
|
Chris@12
|
253 private function getTraitLastModificationTime(ReflectionClass $reflectionTrait)
|
Chris@12
|
254 {
|
Chris@12
|
255 $fileName = $reflectionTrait->getFileName();
|
Chris@12
|
256
|
Chris@12
|
257 return max(array_merge(
|
Chris@12
|
258 [$fileName ? filemtime($fileName) : 0],
|
Chris@12
|
259 array_map([$this, 'getTraitLastModificationTime'], $reflectionTrait->getTraits())
|
Chris@12
|
260 ));
|
Chris@0
|
261 }
|
Chris@0
|
262 }
|