Completed
Push — master ( 8a6821...f9deab )
by Andreas
04:47
created

FileCacheReader   A

Complexity

Total Complexity 40

Size/Duplication

Total Lines 257
Duplicated Lines 0 %

Importance

Changes 5
Bugs 0 Features 0
Metric Value
eloc 102
dl 0
loc 257
rs 9.2
c 5
b 0
f 0
wmc 40

9 Methods

Rating   Name   Duplication   Size   Complexity  
B getClassAnnotations() 0 29 7
A getPropertyAnnotation() 0 11 3
B getMethodAnnotations() 0 30 7
A clearLoadedAnnotations() 0 3 1
A getClassAnnotation() 0 11 3
B getPropertyAnnotations() 0 30 7
A getMethodAnnotation() 0 11 3
A saveCacheFile() 0 25 5
A __construct() 0 18 4

How to fix   Complexity   

Complex Class

Complex classes like FileCacheReader often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use FileCacheReader, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\Common\Annotations;
21
22
/**
23
 * File cache reader for annotations.
24
 *
25
 * @author Johannes M. Schmitt <[email protected]>
26
 * @author Benjamin Eberlei <[email protected]>
27
 *
28
 * @deprecated the FileCacheReader is deprecated and will be removed
29
 *             in version 2.0.0 of doctrine/annotations. Please use the
30
 *             {@see \Doctrine\Common\Annotations\CachedReader} instead.
31
 */
32
class FileCacheReader implements Reader
33
{
34
    /**
35
     * @var Reader
36
     */
37
    private $reader;
38
39
    /**
40
     * @var string
41
     */
42
    private $dir;
43
44
    /**
45
     * @var bool
46
     */
47
    private $debug;
48
49
    /**
50
     * @var array
51
     */
52
    private $loadedAnnotations = [];
53
54
    /**
55
     * @var array
56
     */
57
    private $classNameHashes = [];
58
59
    /**
60
     * @var int
61
     */
62
    private $umask;
63
64
    /**
65
     * Constructor.
66
     *
67
     * @param Reader  $reader
68
     * @param string  $cacheDir
69
     * @param boolean $debug
70
     *
71
     * @throws \InvalidArgumentException
72
     */
73
    public function __construct(Reader $reader, $cacheDir, $debug = false, $umask = 0002)
74
    {
75
        if ( ! is_int($umask)) {
76
            throw new \InvalidArgumentException(sprintf(
77
                'The parameter umask must be an integer, was: %s',
78
                gettype($umask)
79
            ));
80
        }
81
82
        $this->reader = $reader;
83
        $this->umask = $umask;
84
85
        if (!is_dir($cacheDir) && !@mkdir($cacheDir, 0777 & (~$this->umask), true)) {
86
            throw new \InvalidArgumentException(sprintf('The directory "%s" does not exist and could not be created.', $cacheDir));
87
        }
88
89
        $this->dir   = rtrim($cacheDir, '\\/');
90
        $this->debug = $debug;
91
    }
92
93
    /**
94
     * {@inheritDoc}
95
     */
96
    public function getClassAnnotations(\ReflectionClass $class)
97
    {
98
        if ( ! isset($this->classNameHashes[$class->name])) {
99
            $this->classNameHashes[$class->name] = sha1($class->name);
100
        }
101
        $key = $this->classNameHashes[$class->name];
102
103
        if (isset($this->loadedAnnotations[$key])) {
104
            return $this->loadedAnnotations[$key];
105
        }
106
107
        $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
108
        if (!is_file($path)) {
109
            $annot = $this->reader->getClassAnnotations($class);
110
            $this->saveCacheFile($path, $annot);
111
            return $this->loadedAnnotations[$key] = $annot;
112
        }
113
114
        if ($this->debug
115
            && (false !== $filename = $class->getFileName())
116
            && filemtime($path) < filemtime($filename)) {
117
            @unlink($path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

117
            /** @scrutinizer ignore-unhandled */ @unlink($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
118
119
            $annot = $this->reader->getClassAnnotations($class);
120
            $this->saveCacheFile($path, $annot);
121
            return $this->loadedAnnotations[$key] = $annot;
122
        }
123
124
        return $this->loadedAnnotations[$key] = include $path;
125
    }
126
127
    /**
128
     * {@inheritDoc}
129
     */
130
    public function getPropertyAnnotations(\ReflectionProperty $property)
131
    {
132
        $class = $property->getDeclaringClass();
133
        if ( ! isset($this->classNameHashes[$class->name])) {
134
            $this->classNameHashes[$class->name] = sha1($class->name);
135
        }
136
        $key = $this->classNameHashes[$class->name].'$'.$property->getName();
137
138
        if (isset($this->loadedAnnotations[$key])) {
139
            return $this->loadedAnnotations[$key];
140
        }
141
142
        $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
143
        if (!is_file($path)) {
144
            $annot = $this->reader->getPropertyAnnotations($property);
145
            $this->saveCacheFile($path, $annot);
146
            return $this->loadedAnnotations[$key] = $annot;
147
        }
148
149
        if ($this->debug
150
            && (false !== $filename = $class->getFilename())
151
            && filemtime($path) < filemtime($filename)) {
152
            @unlink($path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

152
            /** @scrutinizer ignore-unhandled */ @unlink($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
153
154
            $annot = $this->reader->getPropertyAnnotations($property);
155
            $this->saveCacheFile($path, $annot);
156
            return $this->loadedAnnotations[$key] = $annot;
157
        }
158
159
        return $this->loadedAnnotations[$key] = include $path;
160
    }
161
162
    /**
163
     * {@inheritDoc}
164
     */
165
    public function getMethodAnnotations(\ReflectionMethod $method)
166
    {
167
        $class = $method->getDeclaringClass();
168
        if ( ! isset($this->classNameHashes[$class->name])) {
169
            $this->classNameHashes[$class->name] = sha1($class->name);
170
        }
171
        $key = $this->classNameHashes[$class->name].'#'.$method->getName();
172
173
        if (isset($this->loadedAnnotations[$key])) {
174
            return $this->loadedAnnotations[$key];
175
        }
176
177
        $path = $this->dir.'/'.strtr($key, '\\', '-').'.cache.php';
178
        if (!is_file($path)) {
179
            $annot = $this->reader->getMethodAnnotations($method);
180
            $this->saveCacheFile($path, $annot);
181
            return $this->loadedAnnotations[$key] = $annot;
182
        }
183
184
        if ($this->debug
185
            && (false !== $filename = $class->getFilename())
186
            && filemtime($path) < filemtime($filename)) {
187
            @unlink($path);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

187
            /** @scrutinizer ignore-unhandled */ @unlink($path);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
188
189
            $annot = $this->reader->getMethodAnnotations($method);
190
            $this->saveCacheFile($path, $annot);
191
            return $this->loadedAnnotations[$key] = $annot;
192
        }
193
194
        return $this->loadedAnnotations[$key] = include $path;
195
    }
196
197
    /**
198
     * Saves the cache file.
199
     *
200
     * @param string $path
201
     * @param mixed  $data
202
     *
203
     * @return void
204
     */
205
    private function saveCacheFile($path, $data)
206
    {
207
        if (!is_writable($this->dir)) {
208
            throw new \InvalidArgumentException(sprintf('The directory "%s" is not writable. Both, the webserver and the console user need access. You can manage access rights for multiple users with "chmod +a". If your system does not support this, check out the acl package.', $this->dir));
209
        }
210
211
        $tempfile = tempnam($this->dir, uniqid('', true));
212
213
        if (false === $tempfile) {
214
            throw new \RuntimeException(sprintf('Unable to create tempfile in directory: %s', $this->dir));
215
        }
216
217
        @chmod($tempfile, 0666 & (~$this->umask));
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

217
        /** @scrutinizer ignore-unhandled */ @chmod($tempfile, 0666 & (~$this->umask));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
218
219
        $written = file_put_contents($tempfile, '<?php return unserialize('.var_export(serialize($data), true).');');
220
221
        if (false === $written) {
222
            throw new \RuntimeException(sprintf('Unable to write cached file to: %s', $tempfile));
223
        }
224
225
        @chmod($tempfile, 0666 & (~$this->umask));
226
227
        if (false === rename($tempfile, $path)) {
228
            @unlink($tempfile);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for unlink(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

228
            /** @scrutinizer ignore-unhandled */ @unlink($tempfile);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
229
            throw new \RuntimeException(sprintf('Unable to rename %s to %s', $tempfile, $path));
230
        }
231
    }
232
233
    /**
234
     * {@inheritDoc}
235
     */
236
    public function getClassAnnotation(\ReflectionClass $class, $annotationName)
237
    {
238
        $annotations = $this->getClassAnnotations($class);
239
240
        foreach ($annotations as $annotation) {
241
            if ($annotation instanceof $annotationName) {
242
                return $annotation;
243
            }
244
        }
245
246
        return null;
247
    }
248
249
    /**
250
     * {@inheritDoc}
251
     */
252
    public function getMethodAnnotation(\ReflectionMethod $method, $annotationName)
253
    {
254
        $annotations = $this->getMethodAnnotations($method);
255
256
        foreach ($annotations as $annotation) {
257
            if ($annotation instanceof $annotationName) {
258
                return $annotation;
259
            }
260
        }
261
262
        return null;
263
    }
264
265
    /**
266
     * {@inheritDoc}
267
     */
268
    public function getPropertyAnnotation(\ReflectionProperty $property, $annotationName)
269
    {
270
        $annotations = $this->getPropertyAnnotations($property);
271
272
        foreach ($annotations as $annotation) {
273
            if ($annotation instanceof $annotationName) {
274
                return $annotation;
275
            }
276
        }
277
278
        return null;
279
    }
280
281
    /**
282
     * Clears loaded annotations.
283
     *
284
     * @return void
285
     */
286
    public function clearLoadedAnnotations()
287
    {
288
        $this->loadedAnnotations = [];
289
    }
290
}
291