getTraitLastModificationTime()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 6

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 2
eloc 4
c 2
b 0
f 0
nc 1
nop 1
dl 0
loc 7
ccs 0
cts 5
cp 0
crap 6
rs 10
1
<?php
2
3
namespace yii\annotations;
4
5
use Doctrine\Common\Annotations\Reader;
6
use ReflectionClass;
7
use ReflectionMethod;
8
use ReflectionProperty;
9
10
/**
11
 * Class AnnotationCacheReader
12
 * @package yii\annotations
13
 */
14
final class AnnotationCacheReader implements Reader
15
{
16
    /**
17
     * @var Reader
18
     */
19
    private $delegate;
20
21
    /**
22
     * @var AnnotationCacheInterface
23
     */
24
    private $cache;
25
26
    /**
27
     * @var boolean
28
     */
29
    private $debug;
30
31
    /**
32
     * @var array
33
     */
34
    private $loadedAnnotations = array();
35
36
    /**
37
     * Constructor.
38
     *
39
     * @param Reader $reader
40
     * @param AnnotationCacheInterface  $cache
41
     * @param bool   $debug
42
     */
43 11
    public function __construct(Reader $reader, AnnotationCacheInterface $cache, $debug = false)
44
    {
45 11
        $this->delegate = $reader;
46 11
        $this->cache = $cache;
47 11
        $this->debug = (bool)$debug;
48 11
    }
49
50
    /**
51
     * {@inheritDoc}
52
     */
53 4
    public function getClassAnnotations(ReflectionClass $class)
54
    {
55 4
        $cacheKey = $class->getName();
56
57 4
        if (isset($this->loadedAnnotations[$cacheKey])) {
58
            return $this->loadedAnnotations[$cacheKey];
59
        }
60
61 4
        if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
62 3
            $annots = $this->delegate->getClassAnnotations($class);
63 3
            $this->saveToCache($cacheKey, $annots);
64
        }
65
66 4
        return $this->loadedAnnotations[$cacheKey] = $annots;
67
    }
68
69
    /**
70
     * {@inheritDoc}
71
     */
72 1
    public function getClassAnnotation(ReflectionClass $class, $annotationName)
73
    {
74 1
        foreach ($this->getClassAnnotations($class) as $annot) {
75 1
            if ($annot instanceof $annotationName) {
76 1
                return $annot;
77
            }
78
        }
79
80
        return null;
81
    }
82
83
    /**
84
     * {@inheritDoc}
85
     */
86 4
    public function getPropertyAnnotations(ReflectionProperty $property)
87
    {
88 4
        $class = $property->getDeclaringClass();
89 4
        $cacheKey = $class->getName() . '$' . $property->getName();
90
91 4
        if (isset($this->loadedAnnotations[$cacheKey])) {
92
            return $this->loadedAnnotations[$cacheKey];
93
        }
94
95 4
        if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
96 3
            $annots = $this->delegate->getPropertyAnnotations($property);
97 3
            $this->saveToCache($cacheKey, $annots);
98
        }
99
100 4
        return $this->loadedAnnotations[$cacheKey] = $annots;
101
    }
102
103
    /**
104
     * {@inheritDoc}
105
     */
106 1
    public function getPropertyAnnotation(ReflectionProperty $property, $annotationName)
107
    {
108 1
        foreach ($this->getPropertyAnnotations($property) as $annot) {
109 1
            if ($annot instanceof $annotationName) {
110 1
                return $annot;
111
            }
112
        }
113
114
        return null;
115
    }
116
117
    /**
118
     * {@inheritDoc}
119
     */
120 5
    public function getMethodAnnotations(ReflectionMethod $method)
121
    {
122 5
        $class = $method->getDeclaringClass();
123 5
        $cacheKey = $class->getName() . '#' . $method->getName();
124
125 5
        if (isset($this->loadedAnnotations[$cacheKey])) {
126
            return $this->loadedAnnotations[$cacheKey];
127
        }
128
129 5
        if (false === ($annots = $this->fetchFromCache($cacheKey, $class))) {
130 4
            $annots = $this->delegate->getMethodAnnotations($method);
131 4
            $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 1
    public function clearLoadedAnnotations(): void
157
    {
158 1
        $this->loadedAnnotations = array();
159 1
    }
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 9
    private function fetchFromCache($cacheKey, ReflectionClass $class)
170
    {
171 9
        if ((($data = $this->cache->fetch($cacheKey)) !== false) && $this->isCanReturnCacheData($cacheKey, $class)) {
172 1
            return $data;
173
        }
174
175 8
        return false;
176
    }
177
178
    /**
179
     * @param $cacheKey
180
     * @param ReflectionClass $class
181
     * @return bool
182
     */
183 1
    private function isCanReturnCacheData($cacheKey, ReflectionClass $class): bool
184
    {
185 1
        return (!$this->debug || $this->isCacheFresh($cacheKey, $class));
186
    }
187
188
    /**
189
     * Saves a value to the cache.
190
     *
191
     * @param string $cacheKey The cache key.
192
     * @param mixed  $value    The value.
193
     *
194
     * @return void
195
     */
196 8
    private function saveToCache($cacheKey, $value): void
197
    {
198 8
        $this->cache->save($cacheKey, $value);
199 8
        if ($this->debug) {
200
            $this->cache->save('[C]' . $cacheKey, time());
201
        }
202 8
    }
203
204
    /**
205
     * Checks if the cache is fresh.
206
     *
207
     * @param string           $cacheKey
208
     * @param ReflectionClass $class
209
     *
210
     * @return boolean
211
     */
212
    private function isCacheFresh($cacheKey, ReflectionClass $class): bool
213
    {
214
        $lastModification = $this->getLastModification($class);
215
        if (null === $lastModification) {
216
            return true;
217
        }
218
219
        return $this->cache->fetch('[C]' . $cacheKey) >= $lastModification;
220
    }
221
222
    /**
223
     * Returns the time the class was last modified, testing traits and parents
224
     *
225
     * @param ReflectionClass $class
226
     * @return mixed
227
     */
228
    private function getLastModification(ReflectionClass $class)
229
    {
230
        $filename = $class->getFileName();
231
        $parent   = $class->getParentClass();
232
233
        return max(array_merge(
234
            [$filename ? filemtime($filename) : 0],
235
            array_map([$this, 'getTraitLastModificationTime'], $class->getTraits()),
236
            array_map([$this, 'getLastModification'], $class->getInterfaces()),
237
            $parent ? [$this->getLastModification($parent)] : []
238
        ));
239
    }
240
241
    /**
242
     * @param ReflectionClass $reflectionTrait
243
     * @return mixed
244
     */
245
    private function getTraitLastModificationTime(ReflectionClass $reflectionTrait)
246
    {
247
        $fileName = $reflectionTrait->getFileName();
248
249
        return max(array_merge(
250
            [$fileName ? filemtime($fileName) : 0],
251
            array_map([$this, 'getTraitLastModificationTime'], $reflectionTrait->getTraits())
252
        ));
253
    }
254
}
255