Issues (152)

lib/Doctrine/Annotations/CachedReader.php (2 issues)

1
<?php
2
3
namespace Doctrine\Annotations;
4
5
use Doctrine\Common\Cache\Cache;
6
use ReflectionClass;
7
8
/**
9
 * A cache aware annotation reader.
10
 *
11
 * @author Johannes M. Schmitt <[email protected]>
12
 * @author Benjamin Eberlei <[email protected]>
13
 */
14
final class CachedReader implements Reader
15
{
16
    /**
17
     * @var Reader
18
     */
19
    private $delegate;
20
21
    /**
22
     * @var Cache
23
     */
24
    private $cache;
25
26
    /**
27
     * @var boolean
28
     */
29
    private $debug;
30
31
    /**
32
     * @var array
33
     */
34
    private $loadedAnnotations = [];
35
36
    /**
37
     * Constructor.
38
     *
39
     * @param Reader $reader
40
     * @param Cache  $cache
41
     * @param bool   $debug
42
     */
43 39
    public function __construct(Reader $reader, Cache $cache, $debug = false)
44
    {
45 39
        $this->delegate = $reader;
46 39
        $this->cache = $cache;
47 39
        $this->debug = (boolean) $debug;
48 39
    }
49
50
    /**
51
     * {@inheritDoc}
52
     */
53 19
    public function getClassAnnotations(ReflectionClass $class)
54
    {
55 19
        $cacheKey = $class->getName();
56
57 19
        if (isset($this->loadedAnnotations[$cacheKey])) {
58 1
            return $this->loadedAnnotations[$cacheKey];
59
        }
60
61 19
        if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
62 18
            $annots = $this->delegate->getClassAnnotations($class);
63 14
            $this->saveToCache($cacheKey, $annots);
64
        }
65
66 15
        return $this->loadedAnnotations[$cacheKey] = $annots;
67
    }
68
69
    /**
70
     * {@inheritDoc}
71
     */
72 2
    public function getClassAnnotation(ReflectionClass $class, $annotationName)
73
    {
74 2
        foreach ($this->getClassAnnotations($class) as $annot) {
75 2
            if ($annot instanceof $annotationName) {
76 2
                return $annot;
77
            }
78
        }
79
80
        return null;
81
    }
82
83
    /**
84
     * {@inheritDoc}
85
     */
86 19
    public function getPropertyAnnotations(\ReflectionProperty $property)
87
    {
88 19
        $class = $property->getDeclaringClass();
89 19
        $cacheKey = $class->getName().'$'.$property->getName();
90
91 19
        if (isset($this->loadedAnnotations[$cacheKey])) {
92 2
            return $this->loadedAnnotations[$cacheKey];
93
        }
94
95 19
        if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
96 19
            $annots = $this->delegate->getPropertyAnnotations($property);
97 12
            $this->saveToCache($cacheKey, $annots);
98
        }
99
100 12
        return $this->loadedAnnotations[$cacheKey] = $annots;
101
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106 3
    public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
107
    {
108 3
        foreach ($this->getPropertyAnnotations($property) as $annot) {
109 3
            if ($annot instanceof $annotationName) {
110 3
                return $annot;
111
            }
112
        }
113
114
        return null;
115
    }
116
117
    /**
118
     * {@inheritDoc}
119
     */
120 9
    public function getMethodAnnotations(\ReflectionMethod $method)
121
    {
122 9
        $class = $method->getDeclaringClass();
123 9
        $cacheKey = $class->getName().'#'.$method->getName();
124
125 9
        if (isset($this->loadedAnnotations[$cacheKey])) {
126 1
            return $this->loadedAnnotations[$cacheKey];
127
        }
128
129 9
        if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
130 9
            $annots = $this->delegate->getMethodAnnotations($method);
131 5
            $this->saveToCache($cacheKey, $annots);
132
        }
133
134 5
        return $this->loadedAnnotations[$cacheKey] = $annots;
135
    }
136
137
    /**
138
     * {@inheritDoc}
139
     */
140 1
    public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
141
    {
142 1
        foreach ($this->getMethodAnnotations($method) as $annot) {
143 1
            if ($annot instanceof $annotationName) {
144 1
                return $annot;
145
            }
146
        }
147
148
        return null;
149
    }
150
151
    /**
152
     * Clears loaded annotations.
153
     *
154
     * @return void
155
     */
156
    public function clearLoadedAnnotations()
157
    {
158
        $this->loadedAnnotations = [];
159
    }
160
161
    /**
162
     * Fetches a value from the cache.
163
     *
164
     * @param string          $cacheKey The cache key.
165
     * @param ReflectionClass $class    The related class.
166
     *
167
     * @return mixed The cached value or false when the value is not in cache.
168
     */
169 39
    private function fetchFromCache($cacheKey, ReflectionClass $class)
170
    {
171 39
        if (($data = $this->cache->fetch($cacheKey)) !== false) {
172 6
            if (!$this->debug || $this->isCacheFresh($cacheKey, $class)) {
173 1
                return $data;
174
            }
175
        }
176
177 38
        return false;
178
    }
179
180
    /**
181
     * Saves a value to the cache.
182
     *
183
     * @param string $cacheKey The cache key.
184
     * @param mixed  $value    The value.
185
     *
186
     * @return void
187
     */
188 23
    private function saveToCache($cacheKey, $value)
189
    {
190 23
        $this->cache->save($cacheKey, $value);
191 23
        if ($this->debug) {
192 5
            $this->cache->save('[C]'.$cacheKey, time());
193
        }
194 23
    }
195
196
    /**
197
     * Checks if the cache is fresh.
198
     *
199
     * @param string           $cacheKey
200
     * @param ReflectionClass $class
201
     *
202
     * @return boolean
203
     */
204 6
    private function isCacheFresh($cacheKey, ReflectionClass $class)
205
    {
206 6
        if (null === $lastModification = $this->getLastModification($class)) {
0 ignored issues
show
The condition null === $lastModificati...astModification($class) is always false.
Loading history...
207
            return true;
208
        }
209
210 6
        return $this->cache->fetch('[C]'.$cacheKey) >= $lastModification;
211
    }
212
213
    /**
214
     * Returns the time the class was last modified, testing traits and parents
215
     *
216
     * @param ReflectionClass $class
217
     * @return int
218
     */
219 6
    private function getLastModification(ReflectionClass $class)
220
    {
221 6
        $filename = $class->getFileName();
222 6
        $parent   = $class->getParentClass();
223
224 6
        return max(array_merge(
225 6
            [$filename ? filemtime($filename) : 0],
226 6
            array_map([$this, 'getTraitLastModificationTime'], $class->getTraits()),
227 6
            array_map([$this, 'getLastModification'], $class->getInterfaces()),
228 6
            $parent ? [$this->getLastModification($parent)] : []
0 ignored issues
show
$parent is of type ReflectionClass, thus it always evaluated to true.
Loading history...
229
        ));
230
    }
231
232
    /**
233
     * @param ReflectionClass $reflectionTrait
234
     * @return int
235
     */
236 3
    private function getTraitLastModificationTime(ReflectionClass $reflectionTrait)
237
    {
238 3
        $fileName = $reflectionTrait->getFileName();
239
240 3
        return max(array_merge(
241 3
            [$fileName ? filemtime($fileName) : 0],
242 3
            array_map([$this, 'getTraitLastModificationTime'], $reflectionTrait->getTraits())
243
        ));
244
    }
245
}
246