Completed
Push — master ( da65dc...5751ed )
by Tomáš
20:43 queued 16:49
created

Cache   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 310
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 45
c 1
b 0
f 0
lcom 1
cbo 3
dl 0
loc 310
rs 8.3673

5 Methods

Rating   Name   Duplication   Size   Complexity  
A save() 0 5 1
A get() 0 13 3
A set() 0 9 2
A getSize() 0 5 1
F load() 0 214 38

How to fix   Complexity   

Complex Class

Complex classes like Cache 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 Cache, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * Function for caching between runs.
4
 *
5
 * @author    Greg Sherwood <[email protected]>
6
 * @copyright 2006-2015 Squiz Pty Ltd (ABN 77 084 670 600)
7
 * @license   https://github.com/squizlabs/Symplify\PHP7_CodeSniffer/blob/master/licence.txt BSD Licence
8
 */
9
10
namespace Symplify\PHP7_CodeSniffer\Util;
11
12
use Symplify\PHP7_CodeSniffer\Autoload;
13
use Symplify\PHP7_CodeSniffer\Config;
14
use Symplify\PHP7_CodeSniffer\Ruleset;
15
use Symplify\PHP7_CodeSniffer\Util\Common;
16
17
class Cache
18
{
19
20
    /**
21
     * The filesystem location of the cache file.
22
     *
23
     * @var void
24
     */
25
    private static $path = '';
26
27
    /**
28
     * The cached data.
29
     *
30
     * @var array<string, mixed>
31
     */
32
    private static $cache = array();
33
34
35
    /**
36
     * Loads existing cache data for the run, if any.
37
     *
38
     * @param \Symplify\PHP7_CodeSniffer\Ruleset $ruleset The ruleset used for the run.
39
     * @param \Symplify\PHP7_CodeSniffer\Config  $config  The config data for the run.
40
     *
41
     * @return void
42
     */
43
    public static function load(Ruleset $ruleset, Config $config)
44
    {
45
        // Look at every loaded sniff class so far and use their file contents
46
        // to generate a hash for the code used during the run.
47
        // At this point, the loaded class list contains the core PHPCS code
48
        // and all sniffs that have been loaded as part of the run.
49
        if (PHP_CodeSniffer_VERBOSITY > 1) {
50
            echo PHP_EOL."\tGenerating loaded file list for code hash".PHP_EOL;
51
        }
52
53
        $codeHash = '';
54
        $classes  = array_keys(Autoload::getLoadedClasses());
55
        sort($classes);
56
57
        $installDir     = dirname(__DIR__);
58
        $installDirLen  = strlen($installDir);
59
        $standardDir    = $installDir.DIRECTORY_SEPARATOR.'Standards';
60
        $standardDirLen = strlen($standardDir);
61
        foreach ($classes as $file) {
62
            if (substr($file, 0, $standardDirLen) !== $standardDir) {
63
                if (substr($file, 0, $installDirLen) === $installDir) {
64
                    // We are only interested in sniffs here.
65
                    continue;
66
                }
67
68
                if (PHP_CodeSniffer_VERBOSITY > 1) {
69
                    echo "\t\t=> external file: $file".PHP_EOL;
70
                }
71
            } else if (PHP_CodeSniffer_VERBOSITY > 1) {
72
                echo "\t\t=> internal sniff: $file".PHP_EOL;
73
            }
74
75
            $codeHash .= md5_file($file);
76
        }
77
78
        // Add the content of the used rulesets to the hash so that sniff setting
79
        // changes in the ruleset invalidate the cache.
80
        $rulesets = $ruleset->paths;
81
        sort($rulesets);
82
        foreach ($rulesets as $file) {
83
            if (substr($file, 0, $standardDirLen) !== $standardDir) {
84
                if (PHP_CodeSniffer_VERBOSITY > 1) {
85
                    echo "\t\t=> external ruleset: $file".PHP_EOL;
86
                }
87
            } else if (PHP_CodeSniffer_VERBOSITY > 1) {
88
                echo "\t\t=> internal ruleset: $file".PHP_EOL;
89
            }
90
91
            $codeHash .= md5_file($file);
92
        }
93
94
        // Go through the core PHPCS code and add those files to the file
95
        // hash. This ensures that core PHPCS changes will also invalidate the cache.
96
        // Note that we ignore sniffs here, and any files that don't affect
97
        // the outcome of the run.
98
        $di = new \RecursiveIteratorIterator(
0 ignored issues
show
Unused Code introduced by
$di is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
99
            new \RecursiveDirectoryIterator($installDir),
100
            0,
101
            \RecursiveIteratorIterator::CATCH_GET_CHILD
102
        );
103
104
        $di     = new \RecursiveDirectoryIterator($installDir);
105
        $filter = new \RecursiveCallbackFilterIterator(
106
            $di,
107
            function ($file, $key, $iterator) {
0 ignored issues
show
Unused Code introduced by
The parameter $key is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $iterator is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
108
                // Skip hidden files.
109
                $filename = $file->getFilename();
110
                if (substr($filename, 0, 1) === '.') {
111
                    return false;
112
                }
113
114
                $filePath = Common::realpath($file->getPathname());
115
                if ($filePath === false) {
116
                    return false;
117
                }
118
119
                if (is_dir($filePath) === true
120
                    && ($filename === 'Standards'
121
                    || $filename === 'Exceptions'
122
                    || $filename === 'Reports'
123
                    || $filename === 'Generators')
124
                ) {
125
                    return false;
126
                }
127
128
                return true;
129
            }
130
        );
131
132
        $iterator = new \RecursiveIteratorIterator($filter);
133
        foreach ($iterator as $file) {
134
            if (PHP_CodeSniffer_VERBOSITY > 1) {
135
                echo "\t\t=> core file: $file".PHP_EOL;
136
            }
137
138
            $codeHash .= md5_file($file);
139
        }
140
141
        $codeHash = md5($codeHash);
142
143
        // Along with the code hash, use various settings that can affect
144
        // the results of a run to create a new hash. This hash will be used
145
        // in the cache file name.
146
        $rulesetHash = md5(var_export($ruleset->ignorePatterns, true).var_export($ruleset->includePatterns, true));
147
        $configData  = array(
148
                        'tabWidth'     => $config->tabWidth,
0 ignored issues
show
Documentation introduced by
The property tabWidth does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
149
                        'encoding'     => $config->encoding,
0 ignored issues
show
Documentation introduced by
The property encoding does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
150
                        'recordErrors' => $config->recordErrors,
0 ignored issues
show
Documentation introduced by
The property recordErrors does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
151
                        'codeHash'     => $codeHash,
152
                        'rulesetHash'  => $rulesetHash,
153
                       );
154
155
        $configString = implode(',', $configData);
156
        $cacheHash    = substr(sha1($configString), 0, 12);
157
158
        if (PHP_CodeSniffer_VERBOSITY > 1) {
159
            echo "\tGenerating cache key data".PHP_EOL;
160
            echo "\t\t=> tabWidth: ".$configData['tabWidth'].PHP_EOL;
161
            echo "\t\t=> encoding: ".$configData['encoding'].PHP_EOL;
162
            echo "\t\t=> recordErrors: ".(int) $configData['recordErrors'].PHP_EOL;
163
            echo "\t\t=> codeHash: ".$configData['codeHash'].PHP_EOL;
164
            echo "\t\t=> rulesetHash: ".$configData['rulesetHash'].PHP_EOL;
165
            echo "\t\t=> cacheHash: $cacheHash".PHP_EOL;
166
        }
167
168
        if ($config->cacheFile !== null) {
0 ignored issues
show
Documentation introduced by
The property cacheFile does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
169
            $cacheFile = $config->cacheFile;
0 ignored issues
show
Documentation introduced by
The property cacheFile does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
170
        } else {
171
            // Determine the common paths for all files being checked.
172
            // We can use this to locate an existing cache file, or to
173
            // determine where to create a new one.
174
            if (PHP_CodeSniffer_VERBOSITY > 1) {
175
                echo "\tChecking possible cache file paths".PHP_EOL;
176
            }
177
178
            $paths = array();
179
            foreach ($config->files as $file) {
0 ignored issues
show
Documentation introduced by
The property files does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
180
                $file = Common::realpath($file);
181
                while ($file !== DIRECTORY_SEPARATOR) {
182
                    if (isset($paths[$file]) === false) {
183
                        $paths[$file] = 1;
184
                    } else {
185
                        $paths[$file]++;
186
                    }
187
188
                    $lastFile = $file;
189
                    $file     = dirname($file);
190
                    if ($file === $lastFile) {
191
                        // Just in case something went wrong,
192
                        // we don't want to end up in an infinite loop.
193
                        break;
194
                    }
195
                }
196
            }
197
198
            ksort($paths);
199
            $paths = array_reverse($paths);
200
201
            $numFiles  = count($config->files);
0 ignored issues
show
Documentation introduced by
The property files does not exist on object<Symplify\PHP7_CodeSniffer\Config>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
202
            $tmpDir    = sys_get_temp_dir();
203
            $cacheFile = null;
204
            foreach ($paths as $file => $count) {
205
                if ($count !== $numFiles) {
206
                    unset($paths[$file]);
207
                    continue;
208
                }
209
210
                $fileHash = substr(sha1($file), 0, 12);
211
                $testFile = $tmpDir.DIRECTORY_SEPARATOR."phpcs.$fileHash.$cacheHash.cache";
212
                if ($cacheFile === null) {
213
                    // This will be our default location if we can't find
214
                    // an existing file.
215
                    $cacheFile = $testFile;
216
                }
217
218
                if (PHP_CodeSniffer_VERBOSITY > 1) {
219
                    echo "\t\t=> $testFile".PHP_EOL;
220
                    echo "\t\t\t * based on shared location: $file *".PHP_EOL;
221
                }
222
223
                if (file_exists($testFile) === true) {
224
                    $cacheFile = $testFile;
225
                    break;
226
                }
227
            }//end foreach
228
229
            if ($cacheFile === null) {
230
                // Unlikely, but just in case $paths is empty for some reason.
231
                $cacheFile = $tmpDir.DIRECTORY_SEPARATOR."phpcs.$cacheHash.cache";
232
            }
233
        }//end if
234
235
        self::$path = $cacheFile;
236
        if (PHP_CodeSniffer_VERBOSITY > 1) {
237
            echo "\t=> Using cache file: ".self::$path.PHP_EOL;
238
        }
239
240
        if (file_exists(self::$path) === true) {
241
            self::$cache = json_decode(file_get_contents(self::$path), true);
0 ignored issues
show
Documentation Bug introduced by
It seems like json_decode(file_get_contents(self::$path), true) of type * is incompatible with the declared type array of property $cache.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
242
243
            // Verify the contents of the cache file.
244
            if (self::$cache['config'] !== $configData) {
245
                self::$cache = array();
246
                if (PHP_CodeSniffer_VERBOSITY > 1) {
247
                    echo "\t* cache was invalid and has been cleared *".PHP_EOL;
248
                }
249
            }
250
        } else if (PHP_CodeSniffer_VERBOSITY > 1) {
251
            echo "\t* cache file does not exist *".PHP_EOL;
252
        }
253
254
        self::$cache['config'] = $configData;
255
256
    }//end load()
257
258
259
    /**
260
     * Saves the current cache to the filesystem.
261
     *
262
     * @return void
263
     */
264
    public static function save()
265
    {
266
        file_put_contents(self::$path, json_encode(self::$cache));
267
268
    }//end save()
269
270
271
    /**
272
     * Retrieves a single entry from the cache.
273
     *
274
     * @param string $key The key of the data to get. If NULL,
275
     *                    everything in the cache is returned.
276
     *
277
     * @return mixed
278
     */
279
    public static function get($key=null)
280
    {
281
        if ($key === null) {
282
            return self::$cache;
283
        }
284
285
        if (isset(self::$cache[$key]) === true) {
286
            return self::$cache[$key];
287
        }
288
289
        return false;
290
291
    }//end get()
292
293
294
    /**
295
     * Retrieves a single entry from the cache.
296
     *
297
     * @param string $key   The key of the data to set. If NULL,
298
     *                      sets the entire cache.
299
     * @param mixed  $value The value to set.
300
     *
301
     * @return void
302
     */
303
    public static function set($key, $value)
304
    {
305
        if ($key === null) {
306
            self::$cache = $value;
0 ignored issues
show
Documentation Bug introduced by
It seems like $value of type * is incompatible with the declared type array of property $cache.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
307
        } else {
308
            self::$cache[$key] = $value;
309
        }
310
311
    }//end set()
312
313
314
    /**
315
     * Retrieves the number of cache entries.
316
     *
317
     * @return int
318
     */
319
    public static function getSize()
320
    {
321
        return (count(self::$cache) - 1);
322
323
    }//end getSize()
324
325
326
}//end class
327